mbassador icon indicating copy to clipboard operation
mbassador copied to clipboard

Issues with proxies and enhanced classes in Spring context

Open arne-vandamme opened this issue 11 years ago • 4 comments

We are using Mbassador extensively as the event bus in a Spring 4 environment with multiple application contexts etc.

I have run into some oddities when using the message bus with listeners that are either aop proxies or cglib enhanced:

  • handlers do not register (subscribe) because the proxy does not have the method annotations (probably in case of an AOP proxy, not cglib)
  • filters get a reference to a proxied or enhanced method and don't find the annotations on the originally declared method

The behavior i'm looking for a in Spring context would be:

  • when subscribing a listener, the target class (not the proxy) is used to determine the handlers
  • filters are run against with the target method metadata
  • execution is done on the outer proxied method

However, using this approach would (i think) remove the ability to do the reverse: using AOP to decorate regular methods with Handler annotations and subscribe them to the event bus. I think the case for the latter is perhaps less common.

Anybody else has experience with this and built solutions around it? Any thoughts on how and where this whould be fixed from a generic mbassador point of view?

I have to checkout mbassador-spring myself, will try to build some test cases we can build on to reproduce/resolve.

arne-vandamme avatar Jul 05 '14 09:07 arne-vandamme

I think that using AOP to decorate regular methods with handler annotations seems a very obscure technique. I think a handler should be annotated directly to explicitly state that it is meant to participate in messaging. It is not a pattern that I would aim to support.

Regarding your problems with proxies: We did run into similar issues when using mbassador in a Spring managed web application. We used mbassador to communicate between differently scoped beans. Bean scoped were implemented as scoped proxies created by cglib.

When you use a Bean PostProcessor to subscribe beans to the bus, then for Session/Request scoped beans there will be an invocation of that post processor for each concrete instance as well as once for the managing proxy. In our case we subscribed the scoped proxy and ignored the beans because we wanted the proxy to take care of delivering the message to the correct session bean (depending on the Thread context). Otherwise we would have had to manage some form of identification mechanism ourselves. But this introduced another problem when session beans were listening to messages that could be sent by a job/asynchronous task (which runs outside the Thread context).

One also needs to be aware that transaction management is handled by proxies as well, so when you want to have your message handlers to participate in transactions (which is a common case). To me it seems, that in order to integrate mbassador well with different Spring scenarios you need some form of rule specification mechanism that allows to match a newly created spring bean and determine whether or not to subscribe it and if the instance itself should be subscribed or a nested object. You might have rules like: AnnotatedWith(@Session) -> ProxyOnly, AnnotatedWith(@Request) AND AnnotatedWith(@IgnoreProxy) -> Not the proxy but the instances.

I think it will be not very trivial to determine what kind of object you are actually looking at. We used a very ugly hack to check for CGLIB (look for the substring 'cglib' in the class name...urghhs). There must be cleaner ways and with time it may be possible to establish a set of meaningful and composable rules.

BTW: cglib proxies don't loose annotations as long as they are @Inherited which all of mbassadors annotations are. So I suspect that lost annotation relate to another AOP implementation technique.

bennidi avatar Jul 05 '14 10:07 bennidi

Hm, i see the situation with scoped beans. However, it seems to me that the purpose of the proxies/enhanced beans would be that you use them transparently. So - in almost all cases - subscribe/unsubscribe and publishing should behave as expected, because i don't know (or need to) that i'm dealing with a proxy.

So then it should always be the proxy that is executed and filters should not be influenced by them.

The problem you described seems a bit odd to me. What would be the relevant use case for Session beans to listen to non-session related messages? Would it not be up to the application design to make sure that those only listen to messages they can handle?

An annotation to force the instance to be used in all circumstances could be useful and perhaps not too difficult to achieve. I'll have to think this through still :-)

BTW: Spring has some good utility classes to detect and work with proxies, I'm creating a new branch on my mbassador-spring fork, trying out some stuff there. There are indeed several proxy mechanism, i'll try to cover most of them.

arne-vandamme avatar Jul 05 '14 14:07 arne-vandamme

@bennidi I am interested in your hack to check for CGLIB. I wrote a simple BeanPostProcessor for auto registration but it doesn't seem to ever get passed the CGLIB enhanced class. Maybe this is a change in spring behavior? I'm using spring 5

cyberoblivion avatar Nov 07 '17 18:11 cyberoblivion

The BeanPostProcessor I wrote was for Spring 3, I think. I assume that a lot of behaviour changed within the past releases (of Spring and CGLIB). I found my way to the right class with a break point in the main method of the BeanPostProcessor and inspected the instances which were handed to it. Took me a while because I did not create a sample app but used the complex production app. I suggest you make a minimal example and debug your way through then. Have fun! :)

bennidi avatar Nov 08 '17 10:11 bennidi