View Full Version : AOP Publisher?
kmurphy
Jun 22nd, 2008, 10:29 AM
I'm trying to use the Publisher annotation to create a simple event based handler (that is, the method I'm interested in has Publisher on it, and my handler has Subscribe on it), but no matter what I do I can't seem to get the Publisher method to do anything. I tried running the code through a debugger and it doesn't look like the object with the Publisher annotation on its method is being proxied in any way which I'm fairly certain is the problem. I checked and I have the aopalliance.jar, spring.jar (which contains all the spring-aop.jar contents), and cglib-no-deps.jar in my classpath, but it still doesn't work. Prior to including Spring Integration the project was already annotation driven and all the other annotations seem to be working fine (both beans with the Publisher and Subscriber annotations are spring injected using Autowired) so I can't understand why if the Publisher annotation is working like it's supposed to that the bean with that annotation isn't being proxied. Did I miss a dependency somewhere?
Excerpt from applicationContext.xml:
<integration:annotation-driven/>
<integration:message-bus auto-create-channels="true" channel-factory="queueChannelFactory" error-channel="errorChannel"/>
...
<context:component-scan base-package="paths to scan"/>
P.S. The forum software on here seems to think anything with an at symbol in it is a link.
JonasPartner
Jun 23rd, 2008, 02:31 AM
What makes you think this is not being proxied?
Did you add @Component to your publisher?
Below is a sample I tested against head.
Regards
Jonas
@Component
public class PublisherBean {
public PublisherBean(){
System.out.println("publisher created");
}
@Publisher(channel="messageChannel")
public String getMessage(){
return (new Date()).toString() + " hello";
}
}
public class PublisherDemo {
public static void main(String[] args){
ApplicationContext appCtx = new ClassPathXmlApplicationContext("annotationDemo.xml", PublisherDemo.class);
PublisherBean publisher = (PublisherBean)appCtx.getBean("publisherBean");
publisher.getMessage();
publisher.getMessage();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<message-bus />
<annotation-driven />
<context:component-scan base-package="org.springframework.integration.samples.annotation s"/>
<channel id="messageChannel"/>
<console-target id="console"/>
<target-endpoint target="console" input-channel="messageChannel"></target-endpoint>
</beans:beans>
iwein
Jun 23rd, 2008, 02:59 AM
<context:component-scan base-package="paths to scan"/>
There's your problem. You have to specify the proper path ("com.yourcompany.yourpackage") in the component scan, otherwise your component won't be picked up.
You can try to wire the bean in the context using <bean:bean .../>. Then you'll be sure that the proxying works like it should.
What really makes me scratch my head though is how you can debug something that isn't being invoked... or how you can have it invoked if it wasn't proxied. I must be missing something. Does Jonas' config work for you?
kmurphy
Jun 23rd, 2008, 07:52 AM
There's your problem. You have to specify the proper path ("com.yourcompany.yourpackage") in the component scan, otherwise your component won't be picked up.
That was purely for example, the actual application context has the full path (actually several comma delimited) specified and I know it's working because all the beans are being injected properly. Almost all the components of the project use one of the Component annotations and they're injected into each other using the Autowired annotation on various properties.
What really makes me scratch my head though is how you can debug something that isn't being invoked... or how you can have it invoked if it wasn't proxied. I must be missing something. Does Jonas' config work for you?
It is being invoked, because the bean (the one that should be proxied) is being injected into another one and the method with the Publisher annotation is being called. When I breakpoint on that method though, there's no trace of the proxy in the call stack (and my Subscriber annotated method never gets called). I know what a java or cglib proxy looks like when you're doing debugging as I've worked with AOP before, but for whatever reason it seems like the around advice isn't being applied. I haven't tried the code Jonas provided yet, but that will be my next test.
kmurphy
Jun 23rd, 2008, 08:29 AM
Well, I just tried Jonas code, and it works using all the same libraries as included in my project. I even modified it to make it more like my code by removing the retrieval and invocation of PublisherBean from the main method and instead injecting the bean inside another one and calling its getMessage method in a PostConstruct annotated method. I can see when I debug this one that it is being proxied by cglib, which makes me wonder what I'm doing wrong in my project that's preventing the proxy from being created as the code is nearly identical.
Ok, inside my main method I've got a block of code that looks more or less like this:
log.debug("Instantiating ApplicationContext from " + applicationContext);
this.context = new ClassPathXmlApplicationContext(applicationContext) ;
log.debug("Registering shutdown hooks.");
context.registerShutdownHook();
Then on the bean I'm trying to make into a publisher I have Component and 'Publisher(channel="selectionChannel")' annotations. On another bean in the project I have:
@Autowired
private PublisherBean publisherBean;
Which in that beans PostConstruct annotated method injects that bean into another one. However, when I breakpoint inside that bean to see what's being injected into the other one, it's the raw PublisherBean without a cglib proxy around it. In contrast when I breakpoint the code Jonas posted at that same point (I reworked the code so it injects the PublisherBean into another bean) I can clearly see a cglib proxy around PublisherBean.
This really has me scratching my head. I can't understand why, when using exactly the same libraries, and more or less exactly the same code in the various classes, I'm getting two completely different results. I even checked to see how the applicationContext.xml files differ and they're virtually identical.
kmurphy
Jun 23rd, 2008, 08:32 AM
If you're curious the extra bean I added to Jonas code follows (and works apparently):
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autow ired;
import org.springframework.stereotype.Component;
@Component
public class InjectionTest {
@Autowired
private PublisherBean publisherBean;
@PostConstruct
public void init() {
publisherBean.getMessage();
}
}
Mark Fisher
Jun 23rd, 2008, 08:40 AM
Which in that beans PostConstruct annotated method injects that bean into another one. However, when I breakpoint inside that bean to see what's being injected into the other one, it's the raw PublisherBean without a cglib proxy around it.
Can you post the code for your PostConstruct-annotated method? (the original, not the modified version of Jonas' code). Based on the quote above, it sounds like you may be passing a 'this' reference to another bean?
Regards,
Mark
kmurphy
Jun 23rd, 2008, 08:54 AM
Can you post the code for your PostConstruct-annotated method? (the original, not the modified version of Jonas' code).
It's pretty big, but I can post the relevant parts. Also, because the forum software on here treats anything with an at symbol in it as a link I can't post the annotations properly.
Autowired
private PublisherBean publisherBean;
...
SuppressWarnings("unused")
PostConstruct
private void doInit() {
initComponents();
resetModel();
this.validate();
setWriteable();
}
...
private void initTableGroups() {
...
jTable.getSelectionModel().addListSelectionListene r(
publisherBean);
...
}
When I breakpoint on the line inside the initTableGroups() method and check this.publisherBean it's not wrapped in a cglib proxy like it should be (incidentally my PublisherBean is a ListSelectionListener). I could understand maybe if the class was final, although if I remember trying to proxy a final class throws an exception at runtime, but the class isn't final so that can't be the problem.
kmurphy
Jun 23rd, 2008, 11:25 AM
Well, I found my problem. The method with the Publisher annotation was private which was preventing it from being proxied. I can confirm it's being proxied now, but it's also blowing up when spring tries to inject it, so I've got more digging to do to figure out what exactly the problem is. I'm pretty sure the problem is in something I did, but if not I'll post a reply with the details of what I find.
Edit: The object doesn't get proxied if the method is made protected. It's my understanding (and the docs seem to confirm this), that protected methods should be eligible for proxying, and only private methods are ineligible. Also seems to be some problem with injection of the proxy. I get a java.lang.IllegalArgumentException thrown during injection of the proxied bean complaining that the field can't be set to $Proxy13. I tried forcing object proxying using cglib by setting the <aop:config proxy-target-class="true"/> element in the applicationContext but that doesn't seem to have made a bit of difference. None of the classes or fields are final so that shouldn't be a problem either. I notice that even after I try to force using cglib it still seems like java proxies are being used (the exception is occuring at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllega lArgumentException(UnsafeFieldAccessorImpl.java:14 6)), could spring integration be forcing the use of java proxies? The AOP docs say that all the AOP configs are merged at runtime, so that setting cglib proxying will apply to all uses of AOP. I feel like I'm missing something simple but important here.
kmurphy
Jun 23rd, 2008, 01:43 PM
I extracted most of the methods to an interface and it appears to be working now. I verified the issue on the code Jonas published by having PublisherBean implement an interface (Serializable and Observer) and it appears that if the class being annotated implements any interfaces, then java proxies are used instead of cglib proxies, which then prevents any non-interface methods from being advised. This will also prevent injection of the bean into any other beans expecting the concrete version of the class rather than one of its interfaces. I looked at the source code for Spring Integration briefly and it looks like maybe this is a bug with the way AopUtils does things?
JonasPartner
Jun 24th, 2008, 02:10 AM
I have created INT-263 (http://jira.springframework.org/browse/INT-263) I believe this is an issue with the use of ProxyFactory in PublisherAnnotationPostProcessor
Regards
Jonas
SpringSource
iwein
Jun 24th, 2008, 02:40 AM
I extracted most of the methods to an interface and it appears to be working now. I verified the issue on the code Jonas published by having PublisherBean implement an interface (Serializable and Observer) and it appears that if the class being annotated implements any interfaces, then java proxies are used instead of cglib proxies, which then prevents any non-interface methods from being advised. This will also prevent injection of the bean into any other beans expecting the concrete version of the class rather than one of its interfaces. I looked at the source code for Spring Integration briefly and it looks like maybe this is a bug with the way AopUtils does things?
It sounds like a bug on a first glance. Thanks a lot for the detailed investigation.
JonasPartner
Jun 24th, 2008, 02:43 AM
I created INT-263 for this.
My original post with this seems to be awaiting moderation since it has a link.
Jonas
SpringSource
iwein
Jun 25th, 2008, 07:01 AM
http://jira.springframework.org/browse/INT-263 no moderation for me...
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.