Package org.apache.geronimo.spring

Source Code of org.apache.geronimo.spring.SpringGBean$GeronimoBeanFactory

/**
*
* Copyright 2003-2004 The Apache Software Foundation
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.apache.geronimo.spring;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import net.sf.cglib.proxy.InterfaceMaker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.kernel.Kernel;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

/**
* A GBean for creating graphs of Spring POJOs and auto-deploying them inside Geronimo as GBeans
*
* @version $Rev: 154696 $
*/
public class SpringGBean
  implements GBeanLifecycle
{
  protected static final Log _log = LogFactory.getLog(SpringGBean.class);

  // injected into ctor
  protected final Kernel      _kernel;
  protected final String      _objectName;
  protected final ClassLoader _classLoader;
  protected final URI[]       _classPath;
  protected final URL         _configurationBaseUrl;
  protected final URI         _configPath;

  protected ClassLoader                _appClassLoader;
  protected ObjectName                 _jmxName;
  protected DefaultListableBeanFactory _factory;

  //----------------------------------------
  public static final GBeanInfo GBEAN_INFO;

  static
  {
    GBeanInfoBuilder infoBuilder = new GBeanInfoBuilder("Spring Application Context", SpringGBean.class);

    infoBuilder.addAttribute("kernel"               , Kernel.class      , false);
    infoBuilder.addAttribute("objectName"           , String.class      , false);
    infoBuilder.addAttribute("classLoader"          , ClassLoader.class , false);
    infoBuilder.addAttribute("classPath"            , URI[].class       , true);
    infoBuilder.addAttribute("configurationBaseUrl" , URL.class         , true);
    infoBuilder.addAttribute("configPath"           , URI.class         , true);

    infoBuilder.setConstructor(new String[]{
         "kernel",
         "objectName",
         "classLoader",
         "classPath",
         "configurationBaseUrl",
         "configPath"
             });

    GBEAN_INFO = infoBuilder.getBeanInfo();
  }

  public static GBeanInfo getGBeanInfo() {return GBEAN_INFO;}

  //----------------------------------------


  public
    SpringGBean(Kernel kernel, String objectName, ClassLoader classLoader, URI[] classPath, URL configurationBaseUrl, URI configPath)
  {
    _kernel               =kernel;
    _objectName           =objectName;
    _configPath           =configPath;
    _classLoader          =classLoader;
    _classPath            =classPath;
    _configurationBaseUrl =configurationBaseUrl;
  }

  //----------------------------------------
  // GBeanLifecycle
  //----------------------------------------

  class GeronimoBeanFactory
    extends DefaultListableBeanFactory
  {
    GeronimoBeanFactory(){super();}
  }

  public void
    doStart()
    throws Exception
  {
    _jmxName=new ObjectName(_objectName);

    // set up classloader
    URI root = URI.create(_configurationBaseUrl.toString());

    URL[] urls=new URL[_classPath.length];

    for (int i=0; i<_classPath.length; i++)
    {
      URL url=root.resolve(_classPath[i]).toURL();
      _log.info("_classPath["+i+"]: "+url);
      urls[i]=url;
    }

    _appClassLoader=new URLClassLoader(urls, _classLoader);

    // delegate work to Spring framework...
    _factory=new GeronimoBeanFactory();
    XmlBeanDefinitionReader xbdr=new XmlBeanDefinitionReader(_factory);
    xbdr.setBeanClassLoader(_appClassLoader);
    xbdr.loadBeanDefinitions(new ClassPathResource(_configPath.toString(), _appClassLoader));

    // install aspects around Spring Bean initialisation...
    _factory.addBeanPostProcessor(new BeanPostProcessor() {
       public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
         return beforeInitialization(bean, name);
       }

  public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
    return afterInitialization(bean, name);
  }
      });

    // force lazy construction of every bean described... - is there a better way - ROB ?
    String[] ids=_factory.getBeanDefinitionNames();
    int n=ids.length;
    for (int i=n; i>0; i--)
      _factory.getBean(ids[i-1]);

    _log.info("Deployed: "+n+" POJO"+(n==1?"":"s"));
  }


  public void
    doStop()
    throws Exception
  {
    tidyUp();
  }

  public void
    doFail()
  {
    try
    {
      tidyUp();
    }
    catch (Exception e)
    {
      _log.warn("problem decommissioning Spring module: "+_jmxName, e);
    }
  }

  protected void
    tidyUp()
    throws Exception
  {
    // can we put this as a spring aspect around each POJO ? would be better...
    String pattern=_jmxName.getDomain()+":J2EEApplication="+_jmxName.getKeyProperty("J2EEApplication")+",J2EEServer="+_jmxName.getKeyProperty("J2EEServer")+",SpringModule="+_jmxName.getKeyProperty("name")+",j2eeType=SpringBean,*";
    ObjectName on=new ObjectName(pattern);

    // can't we do it like this - much less typo-prone...
    //     Hashtable props  =new Hashtable(_jmxName.getKeyPropertyList());
    //     props.put("SpringModule" , props.get("name"));
    //     props.put("j2eeType"     , "SpringBean");
    //     props.remove("name");
    //     props.put("*", "");
    //     ObjectName on=new ObjectName(_jmxName.getDomain(), props);

    Set peers=_kernel.listGBeans(on);
    for (Iterator i=peers.iterator(); i.hasNext();)
    {
      ObjectName tmp=(ObjectName)i.next();
      try
      {
  _log.info("stopping: "+tmp);
  _kernel.stopGBean(tmp);
  _log.info("unloading: "+tmp);
  _kernel.unloadGBean(tmp);
      }
      catch (Exception e)
      {
  _log.warn("problem decommissioning POJO peer GBean: "+tmp, e);
      }
    }

    _factory.destroySingletons();
  }

  //----------------------------------------
  // aspects around Spring Bean creation...
  //----------------------------------------

  /**
   * Hook to perform action before Spring Bean initialisation.
   */
  protected Object
    beforeInitialization(Object bean, String name)
  {
    return bean;
  }

  /**
   * Hook to perform action after Spring Bean initialisation.
   */
  protected Object
    afterInitialization(Object bean, String name)
    throws BeansException
  {
    // create a GBean peer...
    try
    {
      GBeanData gd=createPOJOGBeanData(bean, name);
      if (gd==null)
        _log.warn("No GBean available for name: " + name + " bean: " + bean);
      else
      {
  _log.info("proxying: "+bean);
  _log.info("loading: "+gd.getName());
  //        _kernel.loadGBeanProxy(bean, gd, _appClassLoader);
        _kernel.loadGBean(gd, _appClassLoader);
  _log.info("starting: "+gd.getName());
  _kernel.startGBean(gd.getName());
      }
    }
    catch (Exception e)
    {
      throw new BeanDefinitionValidationException("Could not load the GBean for name: " + name + " bean: " + bean + ". Reason: " + e, e);
    }
    return bean;
  }

  //----------------------------------------
  // utils...
  //----------------------------------------

  /**
   * Factory method to create an ObjectName for the Spring bean
   *
   * @param name the name of the bean in the Spring config file
   * @return the ObjectName to use for the given Spring bean name
   */
  protected ObjectName
    createObjectName(String name)
    throws MalformedObjectNameException
  {
    Hashtable props  =new Hashtable(_jmxName.getKeyPropertyList());
    props.put("SpringModule" , props.get("name"));
    props.put("j2eeType"     , "SpringBean");
    props.put("name"         , name);
    return new ObjectName(_jmxName.getDomain(), props);
  }

  protected int _count=0// we should use a Spring generated unique name here - TODO

  public static class InvocationHandler
    implements java.lang.reflect.InvocationHandler, java.io.Serializable
  {
    protected Object _pojo;

    public
      InvocationHandler(Object pojo) {_pojo=pojo;}

    public Object
      invoke(Object proxy, Method method, Object[] args)
      throws Throwable
    {
      return _pojo.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(_pojo, args);
    }
  }

  protected synchronized GBeanData
    createPOJOGBeanData(Object bean, String name)
      throws MalformedObjectNameException
  {
    Class c=createProxyClass(bean);
    GBeanInfoBuilder gbif = new GBeanInfoBuilder(c, "POJO["+(_count++)+"]");

    gbif.addAttribute("invocationHandler", java.lang.reflect.InvocationHandler.class, true);
    gbif.setConstructor(new String[]{"invocationHandler"});
    // describe the rest of the POJOs public API
    Set pm=new HashSet();
    Method[] methods=c.getMethods();
    for (int i=0;i<methods.length;i++)
    {
      Method m=methods[i];
      String n=m.getName();
      Class[] pt=m.getParameterTypes();
      Class rt=m.getReturnType();

      // ugly way of collecting Bean property names - maybe we can get this from Spring ?
      if ((n.startsWith("get") && pt.length==0) || (n.startsWith("set") && pt.length==1 && rt==Void.TYPE))
  pm.add(n.substring(3,4).toLowerCase()+n.substring(4));
    }

    //    pm.remove("class"); // do we want this available ?
    gbif.addInterface(c, (String[])pm.toArray(new String[pm.size()]));
    //gbif.addInterface(c);
    GBeanData gbd=new GBeanData(createObjectName(name), gbif.getBeanInfo());
    // ensure the injection of the InvocationHandler into the newly instantiated Proxy
    gbd.setAttribute("invocationHandler"  , new InvocationHandler(bean));

    return gbd;
  }

  // We have to create a proxy here because the kernel only accepts
  // classes from which to instantiate GBeanInstance targets, not
  // instances, which is what we get from Spring. So we create a proxy
  // that can be instantiated and injected with an InvocationHandler
  // which will delegate all calls onto the corresponding method on
  // the bean that we wanted to pass in in the first place. Complex
  // syntactic sugar - eh...!
  protected Class
    createProxyClass(Object pojo)
  {
    InterfaceMaker im=new InterfaceMaker();
    im.add(pojo.getClass())// add all class' public methods...

    // if POJO does not implement GBeanLifeCycle should we implement
    // it and dump/reroute it, to be safe ?

    Class c=im.create();
    return Proxy.getProxyClass(c.getClassLoader(), new Class[] {c});
  }
}
TOP

Related Classes of org.apache.geronimo.spring.SpringGBean$GeronimoBeanFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.