This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

AnnouncementAnnouncement Module

Collapse

No announcement yet.

spring osgi with aspectj dependency injection on prototypePage Title Module

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.

Comment

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:

- 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:

- 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

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:

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:

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.