Announcement Announcement Module
Collapse
No announcement yet.
spring osgi with aspectj dependency injection on prototype Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • spring osgi with aspectj dependency injection on prototype

    I was experimenting with springweaver and I think I have run into a problem when using spring's AnnotationBeanConfigurerAspect from multiple bundles.

    I will try to describe the environment and the issue:
    • it's an eclipse RCP application that uses spring framework and spring DM
    • we use equinox aspects with aspectj ITD to extend our prototype beans (eclipse views, composites, etc) with PropertyChangeSupport (nice thing to have .. all we have to do is mark some class as @Bindable) and it works fine
    • we use equinox aspects and LTW
    • we've started using spring @Configurable and @Autowired in order to mark some objects as spring configurable and it works like a charm if everything is declared in a single bundle under the same context

    The problem:
    • if I introduce another bundle that has it's own spring context and if I try to instantiate an object marked as @Configurable everything seems to work fine if it uses the same spring beans defined in a previously instantiated context. If it references a bean declared in a newly created context AnnotationBeanConfigurerAspect doesn't inject it. It seems to me that since it's a singleton aspect beanfactory gets replaced each time setBeanFactory is called.
    I was browsing JIRA and found following issues:
    Did spring dm server solve these issues? Can someone point me in the right direction?

    Thank you in advance!
    Last edited by iloncar; Jul 6th, 2009, 05:41 AM.

  • #2
    The Greenpages dm Server sample uses @Autowired successfully. We haven't noticed the issue you describe. Have you tried running on dm Server?

    Comment


    • #3
      Thank you for the quick response Glyn. Unfortunately it doesn't help me.

      Let me explain:

      GreenPagesController is using @Autowired annotation, but it is associated to the spring context by scanning classpath. That already works.

      The problem I see occurs when aspectj AnnotationBeanConfigurerAspect is used to autoconfigure prototypes instantied through operator new.

      I'm sorry, I don't know enough about Spring DM (yet, I just downloaded the server and examples), so I can't provide you with a working/failing test case (eclipse projects).

      Let me try to describe it with a gedankedexperiment:

      - create 2 separate bundles (2 applications)
      - as a bundle dependency include *spring.aspects bundle as it contains AnnotationBeanConfigurerAspect, and all other required bundles
      - in each bundle create a new osgi context
      - in each context include:
      Code:
        <context:spring-configured />
        <context:annotation-config />
        <context:load-time-weaver />
      - in each context create 2 different services (Service1 and Service2)
      - in each context create a new controller (Controller1 and Controller2)
      - in each context create different beans (Component1 and Component2) such that each requires Service from it's own bundle:
      Code:
      @Configured
      class Component1 {
          private Service1 service1;
          @Autowired(required=true)
          public void setService1(Service1 p_service1) {
               service1 = p_service1;
          }
      }
      - in each Controller instantiate appropriate component. for example from Controller1:
      Code:
      @Controller
      public class Controller1 {
              @RequestMapping("/run1.htm")
      	public void run1() {
                   Component1 comp1 = new Component1();
                   // at this moment comp1 should be fully initialized and spring should do it's magic : injecting Service1 into comp1 with a help from AnnotationBeanConfigurerAspect 
      	}
      }
      It's probably not something you would do in a web application but in a RCP application that's the way you would instantiate a GUI composite:
      - describe services in spring context (spring DM), export/import services
      - create eclipse views/ composites as usual
      - mark gui components as @Configurable
      - at runtime you woul expect from AnnotationBeanConfigurerAspect to "autowire" spring services into your GUI components


      In eclipse RCP application in order to use AnnotationBeanConfigurerAspect I have to use springweaver written by Martin Lippert (from equinox aspects) and the problem I see is that AnnotationBeanConfigurerAspect is a singleton shared by all spring DM contexts.

      All of the bundle contexts see the same aspect instance and since it implements BeanFactoryAware interface last context that gets intialized overwrites previous one.

      The proper solution seems to be: create a new "OsgiImproved"AnnotationBeanConfigurerAspect bound to the spring context that configures it but I don't know aspectj well enough to do it on my own.

      I would be grateful if somebody would take a look or explain how spring DM solves this issue (if you had the same issue at some time).

      If you think it's not the appropriate place to ask this question I can repost to the AOP forum.

      Comment


      • #4
        Yes, why not try posting on the AOP forum and see if anyone can help?

        If you can capture the problem on dm Server in a reasonably small testcase, please raise a JIRA so we can investigate.

        I suppose the alternative is to step through the relevant code with a debugger and see if you can spot when the behaviour departs from what you might prefer. ;-)

        Comment


        • #5
          update: spring osgi with aspectj dependency injection

          Ok, I was digging in and chatting with Martin Lippert and here are the results (fragments from mail exchange):

          If class is instantiated inside the bundle that has associated spring context we can get the bundle through PackageAdmin service and by using map that associates bundle to the spring context (using either BundleContextAware or spring DM hook) we can retrieve the right spring context and populate our bean.

          Unfortunately, spring provided AnnotationBeanConfigurerAspect is not enough so I have extended it into OsgiAnnotationBeanConfigurerAspect.

          One must use an OSGI specific BeanConfigurerAspect and be careful not to activate aspect provided by spring (it silly but spring seems to instantiate it if it's found on the classpath and I lost some time due to this hidden side effect).

          Also, currently I depend on custom OsgiBeanFactoryPostProcessor that
          injects BundleContext and ConfigurableListableBeanFactory into OsgiAnnotationBeanConfigurerAspect.



          So, this approach solves the case when bean is instantiated from the bundle that has spring context associated. What about other cases?

          I see two scenarios:

          1. "new" was called directly from the class inside spring enabled bundle - in that case I can easily capture a caller with aspect like:

          Code:
          public aspect OsgiSupportAspect
            perthis(beanConstructorCalled())
          //  pertarget aspect will not work.. probably because we don't see target?
          //	pertarget(beanConstructorCalled())
          {
          	private static final Log LOG = LogFactory.getLog(OsgiSupportAspect.class);
          
          	pointcut beanConstructorCalled() : call((@Configurable *).new(..)) ;
          	Object caller = null;
          
          	{
          		LOG.debug("New aspect created: " + this.toString());
          	}
          
          	before(Object source) : beanConstructorCalled() && this(source)
          	{
          		LOG.debug("created aspect for : " + source);
          		caller  = source;
          	}
          
          	after (Object caller) returning (Object bean):
          		beanConstructorCalled() && this(caller)
          	{
          		OsgiAnnotationBeanConfigurerAspect configurerAspect = OsgiAnnotationBeanConfigurerAspect.aspectOf();
          		configurerAspect.configureBean(caller, bean);
          	}
          
          //	this will not work from perthis aspect
          //	before(): initialization((@Configurable *).new(..))
          //	{
          //		LOG.debug("initialization called");
          //	}
          
          }
          The problem is that I have to do the configuration of the bean before OR after aspectj "initialzation".

          If there were no "preconfigured=true" parameter to the spring bean I
          could just write an after returning advice and configure it using previously populated maps from OsgiAnnotationBeanConfigurerAspect.

          Unfortunately I cannot retrieve this aspect from singleton OsgiAnnotationBeanConfigurerAspect since I don't have access to the caller object so I can't do something like:

          Code:
               // within OsgiAnnotationBeanConfigurerAspect
               // ...
               OsgiSupportAspect.aspectOf(caller);
               //...
          from within OsgiAnnotationBeanConfigurerAspect#configureBean and I'm not completely sure why I can't create pertarget aspect (one that would be associated with @Configurable and created for each invocation of "new" on @Configurable).

          2. "new" or reflective instationation was somehow caused by eclipse platform. I don't have clear understanding of what will happen in this scenario, and it's not really "my problem" since I don't intend/allow to use it in that way.

          In both cases if I could somehow go through the stack trace captured by the pointcut inside OsgiAnnotationBeanConfigurerAspect and have an access to the CLASSES of the stackelement there would be no problem.

          What Martin suggests:
          This sounds a lot more complicated to solve, since its not always easy to identify the calling bundle. To get the class objects of the current execution stack you can create a subclass of security manager and call "getClassContext" (you need to create a subclass to access this method which is protected and you also need to execute this as privileged). This is what the Equinox class "ContextFinder" does when trying to find a bundle to load a class from when the context class loader of the currect thread is used. Then you have the class object directly and you can use PackageAdmin to find out the corresponding bundle, and so on.
          One of the first search results on ContextFinder brought up the post that mentions Glyn so I decided to post on this forum.

          Comment


          • #6
            Here is the first version of the aspect that works in an OSGI environment:

            Code:
            import java.security.AccessController;
            import java.security.PrivilegedAction;
            import java.util.HashMap;
            import java.util.Map;
            import java.util.concurrent.ConcurrentHashMap;
            import java.io.Serializable;
            
            import org.apache.commons.logging.Log;
            import org.apache.commons.logging.LogFactory;
            import org.osgi.framework.Bundle;
            import org.osgi.framework.BundleContext;
            import org.osgi.service.packageadmin.PackageAdmin;
            import org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver;
            import org.springframework.beans.factory.annotation.Configurable;
            import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
            import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
            
            public aspect OsgiAnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect {
              private static final Log LOG = LogFactory.getLog(OsgiAnnotationBeanConfigurerAspect.class);
            
              private ConcurrentHashMap<Bundle, ConfigurableListableBeanFactory> mapOfBeanFactories = new ConcurrentHashMap<Bundle, ConfigurableListableBeanFactory>();
            
              private Map<String, BeanConfigurerSupport> mapOfBeanConfigurers = new HashMap<String, BeanConfigurerSupport>();
            
              static final class Finder extends SecurityManager {
                @SuppressWarnings("unchecked")
                public Class[] getClassContext() {
                  return super.getClassContext();
                }
              }
            
              static ClassLoader finderClassLoader;
              static Finder contextFinder;
            
              static {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                  public Object run() {
                    finderClassLoader = OsgiAnnotationBeanConfigurerAspect.class.getClassLoader();
                    contextFinder = new Finder();
                    return null;
                  }
                });
              }
            
              // spring osgi setters
              public void setBundleBeanFactory(BundleContext p_bundleContext, ConfigurableListableBeanFactory p_beanFactory) {
                Bundle bundle = p_bundleContext.getBundle();
                String bundleName = bundle.getSymbolicName();
                LOG.debug(String.format("Saving into internal mapOfBeanFactories bundle: '%s' beanFactory: '%s'", bundleName,
                    p_beanFactory));
                mapOfBeanFactories.put(bundle, p_beanFactory);
                BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport();
                beanConfigurerSupport.setBeanFactory(p_beanFactory);
                beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
                mapOfBeanConfigurers.put(bundleName, beanConfigurerSupport);
              }
            
              private PackageAdmin m_packageAdmin;
            
              public void setPackageAdmin(PackageAdmin p_packageAdmin) {
                m_packageAdmin = p_packageAdmin;
              }
            
              // configure bean:
              public void configureBean(Object bean) {
                Bundle bundle = m_packageAdmin.getBundle(bean.getClass());
                String bundleName = bundle.getSymbolicName();
                BeanConfigurerSupport beanConfigurerSupport = mapOfBeanConfigurers.get(bundleName);
                if (beanConfigurerSupport != null) {
                  LOG.debug(String.format("configuring bean %s from bundle %s", bean, bundleName));
                  beanConfigurerSupport.configureBean(bean);
                } else {
                  // We have to go through classloader context
                  LOG.debug(String.format("BeanConfigurerSupport not found for bean: %s from bundle %s.", bean, bundle
                      .getSymbolicName()));
                  boolean configured = false;
                  Class[] stack = contextFinder.getClassContext();
                  for (Class klass : stack) {
                    bundle = m_packageAdmin.getBundle(klass);
                    if (bundle != null) {
                      bundleName = bundle.getSymbolicName();
                      LOG.debug(String.format("class '%s' loaded from bundle '%s'!", klass, bundleName));
                      beanConfigurerSupport = mapOfBeanConfigurers.get(bundleName);
                      if (beanConfigurerSupport != null) {
                        configured = false;
                        LOG.debug(String.format("About to configure bean '%s' using spring bean configurer from bundle '%s'.",
                            bean, bundleName));
                        // will this throw an exception if some properties could not be
                        // configured?
                        beanConfigurerSupport.configureBean(bean);
                        configured = true;
                        break;
                      }
                    }
                  }
                  if (!configured) {
                    LOG.debug("Unable to configure bean '%s'.");
                  }
                }
              }
            
              public pointcut inConfigurableBean() : @this(Configurable);
            
              public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*);
            
              declare parents: @Configurable * implements ConfigurableObject;
            
              /*
               * An intermediary to match preConstructionConfiguration signature (that
               * doesn't expose the annotation object)
               */
              private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if(c.preConstruction());
            
              /*
               * This declaration shouldn't be needed, except for an AspectJ bug
               * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=214559)
               */
              declare parents: @Configurable Serializable+
            		implements ConfigurableDeserializationSupport;
            
            }

            Comment


            • #7
              I took another take on the problem. Instead of changing code in all bundles that contain a reference to the AnnotationBeanConfigurerAspect, I build an aspect.

              This aspect intercepts the calls to the important methods of AnnotationBeanConfigurerAspect and redirects them to the correct configurer support instance.

              I am very new to Java, Spring, OSGI and aspect oriented programming. I suspect that the code below needs a significant amount of adjustments, but I hope it presents the idea.


              Greetz Erik

              Code:
              package qirx.aspects;
              
              import java.util.HashMap;
              
              import org.apache.log4j.Logger;
              import org.osgi.framework.Bundle;
              import org.osgi.service.packageadmin.PackageAdmin;
              import org.springframework.beans.BeansException;
              import org.springframework.beans.factory.BeanFactory;
              import org.springframework.beans.factory.BeanFactoryAware;
              import org.springframework.beans.factory.DisposableBean;
              import org.springframework.beans.factory.InitializingBean;
              import org.springframework.beans.factory.NoSuchBeanDefinitionException;
              import org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver;
              import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
              import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
              
              public aspect OsgiAnnotationBeanConfigurerAspect
              {
              	static private Logger _logger = Logger.getLogger(OsgiAnnotationBeanConfigurerAspect.class);
              	
              	/*
              	 * Holds references to the bean configurers. The bundle name is used as key.
              	 */
              	private HashMap<String, BeanConfigurerSupport> _beanConfigurers;
              
              	/*
              	 * Service injected using spring
              	 */
              	private PackageAdmin _packageAdmin;
              	
              	public OsgiAnnotationBeanConfigurerAspect()
              	{
              		_beanConfigurers = new HashMap<String, BeanConfigurerSupport>();
              	}
              
              	/*
              	 * Pointcuts for the 4 methods that are used in the AnnotationBeanConfigurerAspect
              	 */
              	pointcut callConfigureBean(): call(void configureBean(Object)) && target(AnnotationBeanConfigurerAspect);
              	pointcut callSetBeanFactory(): call(void BeanFactoryAware+.setBeanFactory(BeanFactory)) && target(AnnotationBeanConfigurerAspect);
              	pointcut callAfterPropertiesSet(): call(void InitializingBean+.afterPropertiesSet()) && target(AnnotationBeanConfigurerAspect);
              	pointcut callDestroy(): call(void DisposableBean+.destroy()) && target(AnnotationBeanConfigurerAspect);
              	
              	/*
              	 * Advises for the 4 methods that are used in the AnnotationBeanConfigurerAspect.
              	 * 
              	 * They all call a method with the same name. They pass in the name of the bundle 
              	 * this method is called for.
              	 */
              	void around(Object bean): callConfigureBean() && args(bean)
              	{
              		if (getPackageAdmin() == null)
              		{
              			/*
              			 * we do not control the instantiation of this aspect, spring has not jet
              			 * injected the PackageAdmin, so we need to wait for it.
              			 * 
              			 * My lack of knowledge prevents me from finding a more elegant solution.
              			 */
              			int counter = 0;
              			while (getPackageAdmin() == null)
              			{
              				try
              				{
              					Thread.sleep(200);
              				} catch (InterruptedException e)
              				{
              					throw new RuntimeException(e);
              				}
              				
              				if (counter++ > 10)
              				{
              					break;
              				}
              			}
              		}
              		
              		if (getPackageAdmin() == null)
              		{
              			_logger.error("Could not configure bean of type '" + bean.getClass().getName() + "', the package admin was not available");
              		} else
              		{
              			Bundle bundle = getPackageAdmin().getBundle(bean.getClass());
              			
              			String bundleName = null;
              			
              			if (bundle != null)
              			{
              				bundleName = bundle.getSymbolicName();
              			}
              			
              			configureBean(bundleName, bean);
              		}
              	}
              	
              	void around(BeanFactory beanFactory): callSetBeanFactory() && args(beanFactory)
              	{
              		String bundleName = _getBundleName(thisJoinPoint.getThis());
              		setBeanFactory(bundleName, beanFactory);
              	}
              	
              	void around(): callAfterPropertiesSet()
              	{
              		String bundleName = _getBundleName(thisJoinPoint.getThis());
              		try
              		{
              			afterPropertiesSet(bundleName);
              		} catch (Exception e)
              		{
              			throw new RuntimeException(e);
              		}
              	}
              	
              	void around(): callDestroy()
              	{
              		String bundleName = _getBundleName(thisJoinPoint.getThis());
              		try
              		{
              			destroy(bundleName);
              		} catch (Exception e)
              		{
              			throw new RuntimeException(e);
              		}
              	}
              	
              	/**
              	 * If the given caller is of type BeanFactory, a bean with the name 'bundle' is 
              	 * retrieved. The retrieved bundle's getSymbolicName method is used to determine 
              	 * the bundle name.
              	 */
              	private String _getBundleName(Object caller)
              	{
              		String bundleName = null;
              		
              		if (caller instanceof BeanFactory)
              		{
              			try
              			{
              				Bundle bundle = (Bundle) ((BeanFactory) caller).getBean("bundle");
              				bundleName = bundle.getSymbolicName();
              			} catch (NoSuchBeanDefinitionException e)
              			{
              				//ignore the exception
              			}
              		}
              		
              		return bundleName;
              	}
              	
              	/**
              	 * Returns a BeanConfigurerSupport instance based on the bundle name. Makes 
              	 * sure only one BeanConfigurerSupport will be created for each bundle.
              	 */
              	private BeanConfigurerSupport _getBeanConfigurerSupport(String bundleName)
              	{
              		BeanConfigurerSupport beanConfigurerSupport;
              		
              		if (_beanConfigurers.containsKey(bundleName))
              		{
              			beanConfigurerSupport = _beanConfigurers.get(bundleName);
              		} else
              		{
              			if (_logger.isInfoEnabled())
              			{
              				_logger.info("Creating new BeanConfigurerSupport for bundle with name '" + bundleName + "'");
              			}
              			
              			beanConfigurerSupport = new BeanConfigurerSupport();
              			beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
              			
              			_beanConfigurers.put(bundleName, beanConfigurerSupport);
              		}
              		
              		return beanConfigurerSupport;
              	}
              	
              	/**
              	 * Calls the configureBean method on the appropriate bean configurer
              	 */
              	public void configureBean(String bundleName, Object bean) 
              	{
              		if (bundleName == null)
              		{
              			_logger.error("Could not configure bean of type '" + bean.getClass().getName() + "', could not determine bundle");
              		} else
              		{
              			if (_logger.isDebugEnabled())
              			{
              				_logger.debug("Calling configureBean for bean of type '" + bean.getClass().getName() + "' using configurer for bundle with name '" + bundleName + "'");
              			}
              			
              			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
              			beanConfigurerSupport.configureBean(bean);
              		}
              	}
              
              	/**
              	 * Calls the setBeanFactory method on the appropriate bean configurer
              	 */
              	public void setBeanFactory(String bundleName, BeanFactory beanFactory) throws BeansException
              	{
              		if (bundleName == null)
              		{
              			_logger.error("Could not set beanFactory, could not determine bundle");
              		} else
              		{
              			if (_logger.isDebugEnabled())
              			{
              				_logger.debug("Setting bean factory for configurer for bundle with name '" + bundleName + "'");
              			}
              			
              			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
              			beanConfigurerSupport.setBeanFactory(beanFactory);
              		}
              	}
              
              	/**
              	 * Calls the afterPropertiesSet method on the appropriate bean configurer
              	 */
              	public void afterPropertiesSet(String bundleName) throws Exception 
              	{
              		if (bundleName == null)
              		{
              			_logger.error("Could not call afterPropertiesSet, could not determine bundle");
              		} else
              		{
              			if (_logger.isDebugEnabled())
              			{
              				_logger.debug("Calling afterPropertiesSet on configurer for bundle with name '" + bundleName + "'");
              			}
              			
              			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
              			beanConfigurerSupport.afterPropertiesSet();
              		}
              	}
              
              	public void destroy(String bundleName) throws Exception 
              	{
              		if (bundleName == null)
              		{
              			_logger.error("Could not call destroy, could not determine bundle");
              		} else
              		{
              			if (_logger.isDebugEnabled())
              			{
              				_logger.debug("Calling destroy on configurer for bundle with name '" + bundleName + "'");
              			}			
              			
              			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
              			beanConfigurerSupport.destroy();
              			
              			_beanConfigurers.remove(bundleName);
              		}
              	}
              	
              	public void setPackageAdmin(PackageAdmin packageAdmin)
              	{
              		_packageAdmin = packageAdmin;
              	}
              
              	public PackageAdmin getPackageAdmin()
              	{
              		return _packageAdmin;
              	}
              }

              Comment


              • #8
                Updated version with 'destroy' method working correctly.

                Any feedback is still appreciated.


                Greetz Erik

                Code:
                package qirx.aspects;
                
                import java.util.HashMap;
                
                import org.apache.log4j.Logger;
                import org.aspectj.lang.annotation.SuppressAjWarnings;
                import org.osgi.framework.Bundle;
                import org.osgi.service.packageadmin.PackageAdmin;
                import org.springframework.beans.BeansException;
                import org.springframework.beans.factory.BeanFactory;
                import org.springframework.beans.factory.BeanFactoryAware;
                import org.springframework.beans.factory.DisposableBean;
                import org.springframework.beans.factory.InitializingBean;
                import org.springframework.beans.factory.NoSuchBeanDefinitionException;
                import org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver;
                import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
                import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
                
                public aspect OsgiAnnotationBeanConfigurerAspect
                {
                	static private Logger _logger = Logger.getLogger(OsgiAnnotationBeanConfigurerAspect.class);
                	
                	/*
                	 * Holds references to the bean configurers. The bundle name is used as key.
                	 */
                	private HashMap<String, BeanConfigurerSupport> _beanConfigurers;
                
                	/*
                	 * Service injected using spring
                	 */
                	private PackageAdmin _packageAdmin;
                	
                	public OsgiAnnotationBeanConfigurerAspect()
                	{
                		_beanConfigurers = new HashMap<String, BeanConfigurerSupport>();
                	}
                
                	/*
                	 * Pointcuts for the 4 methods that are used in the AnnotationBeanConfigurerAspect
                	 */
                	pointcut callConfigureBean(): call(void configureBean(Object)) && target(AnnotationBeanConfigurerAspect);
                	pointcut callSetBeanFactory(): call(void BeanFactoryAware+.setBeanFactory(BeanFactory)) && target(AnnotationBeanConfigurerAspect);
                	pointcut callAfterPropertiesSet(): call(void InitializingBean+.afterPropertiesSet()) && target(AnnotationBeanConfigurerAspect);
                	pointcut callDestroy(): call(void DisposableBean+.destroy()) && target(AnnotationBeanConfigurerAspect);
                	pointcut callDestroySingletons(Object o): execution(void *.destroySingletons()) && this(o);
                
                	/*
                	 * Advises for the 4 methods that are used in the AnnotationBeanConfigurerAspect. These
                	 * advises prevent default execution.
                	 * 
                	 * They all call a method with the same name. They pass in the name of the bundle 
                	 * this method is called for.
                	 */
                	@SuppressAjWarnings("adviceDidNotMatch") 
                	void around(Object bean): callConfigureBean() && args(bean)
                	{
                		if (getPackageAdmin() == null)
                		{
                			/*
                			 * we do not control the instantiation of this aspect, spring has not jet
                			 * injected the PackageAdmin, so we need to wait for it.
                			 * 
                			 * My lack of knowledge prevents me from finding a more elegant solution.
                			 */
                			int counter = 0;
                			while (getPackageAdmin() == null)
                			{
                				try
                				{
                					Thread.sleep(200);
                				} catch (InterruptedException e)
                				{
                					throw new RuntimeException(e);
                				}
                				
                				if (counter++ > 10)
                				{
                					break;
                				}
                			}
                		}
                		
                		if (getPackageAdmin() == null)
                		{
                			_logger.error("Could not configure bean of type '" + bean.getClass().getName() + "', the package admin was not available");
                		} else
                		{
                			Bundle bundle = getPackageAdmin().getBundle(bean.getClass());
                			
                			String bundleName = null;
                			
                			if (bundle != null)
                			{
                				bundleName = bundle.getSymbolicName();
                			}
                			
                			configureBean(bundleName, bean);
                		}
                	}
                	
                	@SuppressAjWarnings("adviceDidNotMatch")
                	void around(BeanFactory beanFactory): callSetBeanFactory() && args(beanFactory)
                	{
                		String bundleName = _getBundleName(thisJoinPoint.getThis());
                		setBeanFactory(bundleName, beanFactory);
                	}
                	
                	@SuppressAjWarnings("adviceDidNotMatch")
                	void around(): callAfterPropertiesSet()
                	{
                		String bundleName = _getBundleName(thisJoinPoint.getThis());
                		try
                		{
                			afterPropertiesSet(bundleName);
                		} catch (Exception e)
                		{
                			throw new RuntimeException(e);
                		}
                	}
                	
                	@SuppressAjWarnings("adviceDidNotMatch")
                	void around(Object o): callDestroy() && cflow(callDestroySingletons(o))
                	{
                		String bundleName = _getBundleName(o);
                		try
                		{
                			destroy(bundleName);
                		} catch (Exception e)
                		{
                			throw new RuntimeException(e);
                		}
                	}
                	
                	/**
                	 * If the given caller is of type BeanFactory, a bean with the name 'bundle' is 
                	 * retrieved. The retrieved bundle's getSymbolicName method is used to determine 
                	 * the bundle name.
                	 */
                	private String _getBundleName(Object object)
                	{
                		String bundleName = null;
                
                		if (object instanceof BeanFactory)
                		{
                			try
                			{
                				Bundle bundle = (Bundle) ((BeanFactory) object).getBean("bundle");
                				bundleName = bundle.getSymbolicName();
                			} catch (NoSuchBeanDefinitionException e)
                			{
                				//ignore the exception
                			}
                		} else if (object instanceof AnnotationBeanConfigurerAspect)
                		{
                		}
                		
                		if (bundleName == null)
                		{
                			_logger.debug("could not determine bundle for class " + object.getClass().getName());
                		}
                
                		return bundleName;
                	}
                	
                	/**
                	 * Returns a BeanConfigurerSupport instance based on the bundle name. Makes 
                	 * sure only one BeanConfigurerSupport will be created for each bundle.
                	 */
                	private BeanConfigurerSupport _getBeanConfigurerSupport(String bundleName)
                	{
                		BeanConfigurerSupport beanConfigurer;
                		
                		if (_beanConfigurers.containsKey(bundleName))
                		{
                			beanConfigurer = _beanConfigurers.get(bundleName);
                		} else
                		{
                			if (_logger.isInfoEnabled())
                			{
                				_logger.info("Creating new BeanConfigurerSupport for bundle with name '" + bundleName + "'");
                			}
                			
                			beanConfigurer = new BeanConfigurerSupport();
                			beanConfigurer.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
                			
                			_beanConfigurers.put(bundleName, beanConfigurer);
                		}
                		
                		return beanConfigurer;
                	}
                	
                	/**
                	 * Calls the configureBean method on the appropriate bean configurer
                	 */
                	public void configureBean(String bundleName, Object bean) 
                	{
                		if (bundleName == null)
                		{
                			_logger.error("Could not configure bean of type '" + bean.getClass().getName() + "', could not determine bundle");
                		} else
                		{
                			if (_logger.isDebugEnabled())
                			{
                				_logger.debug("Calling configureBean for bean of type '" + bean.getClass().getName() + "' using configurer for bundle with name '" + bundleName + "'");
                			}
                			
                			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
                			beanConfigurerSupport.configureBean(bean);
                		}
                	}
                
                	/**
                	 * Calls the setBeanFactory method on the appropriate bean configurer
                	 */
                	public void setBeanFactory(String bundleName, BeanFactory beanFactory) throws BeansException
                	{
                		if (bundleName == null)
                		{
                			_logger.error("Could not set beanFactory, could not determine bundle");
                		} else
                		{
                			if (_logger.isDebugEnabled())
                			{
                				_logger.debug("Setting bean factory for configurer for bundle with name '" + bundleName + "'");
                			}
                			
                			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
                			beanConfigurerSupport.setBeanFactory(beanFactory);
                		}
                	}
                
                	/**
                	 * Calls the afterPropertiesSet method on the appropriate bean configurer
                	 */
                	public void afterPropertiesSet(String bundleName) throws Exception 
                	{
                		if (bundleName == null)
                		{
                			_logger.error("Could not call afterPropertiesSet, could not determine bundle");
                		} else
                		{
                			if (_logger.isDebugEnabled())
                			{
                				_logger.debug("Calling afterPropertiesSet on configurer for bundle with name '" + bundleName + "'");
                			}
                			
                			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
                			beanConfigurerSupport.afterPropertiesSet();
                		}
                	}
                
                	/**
                	 * Calls the destroy method on the appropriate bean configurer
                	 */
                	public void destroy(String bundleName) throws Exception 
                	{
                		if (bundleName == null)
                		{
                			_logger.error("Could not call destroy, could not determine bundle");
                		} else
                		{
                			if (_logger.isDebugEnabled())
                			{
                				_logger.debug("Calling destroy on configurer for bundle with name '" + bundleName + "'");
                			}			
                			
                			BeanConfigurerSupport beanConfigurerSupport = _getBeanConfigurerSupport(bundleName);
                			beanConfigurerSupport.destroy();
                			
                			_beanConfigurers.remove(bundleName);
                		}
                	}
                	
                	public void setPackageAdmin(PackageAdmin packageAdmin)
                	{
                		_packageAdmin = packageAdmin;
                	}
                
                	public PackageAdmin getPackageAdmin()
                	{
                		return _packageAdmin;
                	}
                }

                Comment

                Working...
                X