Announcement Announcement Module
Collapse
No announcement yet.
AOP Publisher? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AOP Publisher?

    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:

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

  • #2
    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

    Code:
    @Component
    public class PublisherBean {
    	
    	public PublisherBean(){
    		System.out.println("publisher created");
    	}
    	
    	@Publisher(channel="messageChannel")
    	public String getMessage(){
    		return (new Date()).toString() + " hello";
    	}
    
    }
    Code:
    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();
    	}
    
    }
    Code:
    <?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.annotations"/>
    	<channel id="messageChannel"/>
    	<console-target id="console"/>
    	<target-endpoint target="console" input-channel="messageChannel"></target-endpoint>
    	
    </beans:beans>

    Comment


    • #3
      Originally posted by kmurphy View Post
      Code:
      <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?

      Comment


      • #4
        Originally posted by iwein View Post
        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.

        Originally posted by iwein View Post
        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.

        Comment


        • #5
          ?

          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:
          Code:
          		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:
          Code:
          @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.

          Comment


          • #6
            Updated Code

            If you're curious the extra bean I added to Jonas code follows (and works apparently):

            Code:
            import javax.annotation.PostConstruct;
            
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.stereotype.Component;
            
            @Component
            public class InjectionTest {
            	@Autowired
            	private PublisherBean publisherBean;
            	
            	@PostConstruct
            	public void init() {
            		publisherBean.getMessage();
            	}
            }

            Comment


            • #7
              Originally posted by kmurphy View Post
              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

              Comment


              • #8
                Originally posted by Mark Fisher View Post
                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.

                Code:
                	Autowired
                	private PublisherBean publisherBean;
                
                ...
                
                	SuppressWarnings("unused")
                	PostConstruct
                	private void doInit() {
                		initComponents();
                		resetModel();
                		this.validate();
                		setWriteable();
                	}
                
                ...
                
                	private void initTableGroups() {
                
                		...
                
                		jTable.getSelectionModel().addListSelectionListener(
                				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.

                Comment


                • #9
                  working... sort of

                  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.
                  Last edited by kmurphy; Jun 23rd, 2008, 01:17 PM.

                  Comment


                  • #10
                    Progress

                    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?

                    Comment


                    • #11
                      I have created INT-263 I believe this is an issue with the use of ProxyFactory in PublisherAnnotationPostProcessor

                      Regards

                      Jonas

                      SpringSource

                      Comment


                      • #12
                        Originally posted by kmurphy View Post
                        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.

                        Comment


                        • #13
                          I created INT-263 for this.

                          My original post with this seems to be awaiting moderation since it has a link.

                          Jonas

                          SpringSource

                          Comment


                          • #14
                            http://jira.springframework.org/browse/INT-263 no moderation for me...

                            Comment

                            Working...
                            X