Announcement Announcement Module
Collapse
No announcement yet.
ApplicationEvent ties code to Spring Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ApplicationEvent ties code to Spring

    Hi!

    My first post at this forum. Recently discovered Spring and have done some coding at home to try it out, as well as listening to presentations
    on Spring.

    One thing I really liked about Spring is the fact that it doesn't tie your code to a framework. But then I looked into the ApplicationEvents and there you have an interface for the listener and a base class for the events. I also didn't like that fact that you would do an instanceof to check the type of the event in each implementation. So I started to think if there was a way to get around this. And it turned out to be easy with all the features that Spring provides.

    The result; an event publisher and an event dispatcher that removed all ties to Spring from the application code. And obviously the application code wouldn't hold any ties to the dispatcher or the publisher either.

    I can post the code if anyone is interested. In principle what it is:

    The dispatcher:
    A bean that you configure through applicationConfig.xml. The configuration holds a mapping between event object classes and event listeners classes. I.e it uses marker interfaces in line with how Spring normally works, but you use your own marker interfaces. All beans implementing the interface would be notified. It listens to Spring ApplicationEvents and forwards it to the correct listeners.

    The publisher
    This is written as an AOP interceptor. A class that wants to fire an event should write a method that returns an event object (java.util.EventObject). Through the configuration in applicationConfig.xml the publisher is advicing this method and takes the returning event and publishes it to Spring.

    --
    Magnus

  • #2
    Yes, please! I am sure I am not the only one who is interested in your solution. This issue bugged me for ages.

    Lawrence

    Comment


    • #3
      Will post it later today, I am going to make some cleanup and add some more comments so that is becomes understandable.



      --
      Magnus

      Comment


      • #4
        What about code ?

        We are waiting for code !

        Comment


        • #5
          It is coming, it has been a busy week. I haven't forgotten.

          --
          Magnus

          Comment


          • #6
            Here comes the vital parts of the code. It should be seen as a proof of concept there are somethings that I would change before using it for real. It should give you a good understanding behind the principle.

            Dispatcher:
            Code:
            public class SpringEventDispatcher implements ApplicationListener,
                    BeanPostProcessor {
            
                private EventListenerList listeners = new EventListenerList();
                private Map mapping;
            
                public void setInterfaceEventMapping(Map mapping)
                        throws ClassNotFoundException {
            
                    Iterator entries = mapping.entrySet().iterator();
                    while (entries.hasNext()) {
                        Map.Entry entry = (Map.Entry) entries.next();
                        String value = (String) entry.getValue();
                        entry.setValue(Class.forName(value));
                    }
                    this.mapping = mapping;
                }
            
                public void onApplicationEvent(ApplicationEvent event) {
                    if (event instanceof EventWrapper) {
                        EventWrapper wrapper = (EventWrapper) event;
                        EventObject realEvent = wrapper.getWrappedEvent();
            
                        if (mapping.containsKey(realEvent.getClass().getName())) {
                            Class eventInterface = (Class) mapping.
                                                   get(realEvent.getClass().getName());
                            EventListener[] eventListeners = listeners
                                    .getListeners(eventInterface);
                            for &#40;int i = 0; i < eventListeners.length; i++&#41; &#123;
                                callListeners&#40;eventListeners&#91;i&#93;, realEvent, eventInterface&#41;;
                            &#125;
                        &#125;
                    &#125;
                &#125;
            
                public Object postProcessBeforeInitialization&#40;Object bean, String beanName&#41;
                        throws BeansException &#123;
                    Class beanClass = bean.getClass&#40;&#41;;
                    Class&#91;&#93; interfaces = beanClass.getInterfaces&#40;&#41;;
                    for &#40;int i = 0; i < interfaces.length; i++&#41; &#123;
                        if &#40;mapping.containsValue&#40;interfaces&#91;i&#93;&#41;&#41; &#123;
                            listeners.add&#40;interfaces&#91;i&#93;, &#40;EventListener&#41; bean&#41;;
                        &#125;
                    &#125;
                    return bean;
                &#125;
            
                private void callListeners&#40;EventListener target,
                        EventObject event,
                        Class eventInterface&#41; &#123;
            
                    Method&#91;&#93; methods = eventInterface.getDeclaredMethods&#40;&#41;;
                    for &#40;int i = 0; i < methods.length; i++&#41; &#123;
                        Class&#91;&#93; parameters = methods&#91;i&#93;.getParameterTypes&#40;&#41;;
                        if &#40;parameters.length == 1
                                && parameters&#91;0&#93;.equals&#40;event.getClass&#40;&#41;&#41;&#41; &#123;
                            try &#123;
                                methods&#91;i&#93;.invoke&#40;target, new Object&#91;&#93; &#123; event &#125;&#41;;
                            &#125; catch &#40;IllegalArgumentException e&#41; &#123;
                                // Real Exception handling needed here
                                e.printStackTrace&#40;&#41;;
                            &#125; catch &#40;IllegalAccessException e&#41; &#123;
                                // Real Exception handling needed here
                                e.printStackTrace&#40;&#41;;
                            &#125; catch &#40;InvocationTargetException e&#41; &#123;
                                // Real Exception handling needed here
                                e.printStackTrace&#40;&#41;;
                            &#125;
                            return;
                        &#125;
            
                    &#125;
            
            ...
            
                &#125;
            Publisher, I have two variants.
            Code:
            public class SpringEventAdvicePublisher implements ApplicationContextAware,
                    AfterReturningAdvice &#123;
            
                private ApplicationContext ctx;
            
                public void publishEvent&#40;EventObject event&#41;&#123;
                    ctx.publishEvent&#40;new EventWrapper&#40;this, event&#41;&#41;;
                &#125;
            
                public void setApplicationContext&#40;ApplicationContext ctx&#41; &#123;
                    this.ctx = ctx;
                &#125;
            
                public void afterReturning&#40;Object returnValue,
                        				   Method method,
                        				   Object&#91;&#93; arguments,
                        				   Object targetObject&#41; throws Throwable &#123;
                    if&#40;returnValue instanceof EventObject&#41;&#123;
                        publishEvent&#40;&#40;EventObject&#41; returnValue&#41;;
                    &#125;
            
                &#125;
            
            &#125;
            Code:
            public class SpringEventObserverPublisher implements ApplicationContextAware,
                    Observer &#123;
            
                private ApplicationContext ctx;
            
                public void publishEvent&#40;EventObject event&#41; &#123;
                    ctx.publishEvent&#40;new EventWrapper&#40;this, event&#41;&#41;;
                &#125;
            
                public void setApplicationContext&#40;ApplicationContext ctx&#41; &#123;
                    this.ctx = ctx;
                &#125;
            
                public void update&#40;Observable o, Object arg&#41; &#123;
                    if &#40;arg instanceof EventObject&#41; &#123;
                        publishEvent&#40;&#40;EventObject&#41; arg&#41;;
                    &#125;
            
                &#125;
            
            &#125;
            And the Event class:
            Code:
            public class EventWrapper extends ApplicationEvent &#123;
            
                private EventObject wrappedEvent;
            
                public EventWrapper&#40;Object source, EventObject wrappedEvent&#41; &#123;
                    super&#40;source&#41;;
                    this.wrappedEvent = wrappedEvent;
                &#125;
            
                public EventObject getWrappedEvent&#40;&#41;&#123;
                    return wrappedEvent;
                &#125;
            
            &#125;
            Finally some lines from the applicationContext.xml as an example of how
            to set up the Advice publisher. I prefer the Observer variant but the setup for that should be trivial.
            In this case the class uses a fireEvent method to create the event, and the advice will fire that event. So the name fireEvent is somewhat misleading.
            Code:
            <beans>
            	<bean id="dispatcher" class="se.iorlas.SpringEventDispatcher">
            		<property name="interfaceEventMapping">
            			<map>
            				<entry key="se.iorlas.TestEventObject">
            					<value>se.iorlas.TestListenerInterface</value>
            				</entry>
            			</map>
            		</property>
            	</bean>
            	<bean id="publisher" class="se.iorlas.SpringEventPublisher">		
            	</bean>
            	<bean id="testListener" class="se.iorlas.TestListener"></bean>
            	<bean id="testFirererTarget" class="se.iorlas.TestFirerer"></bean>
            	<bean id="fireAdvisor"
            		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            		<property name="advice">
            			<ref local="publisher"/>
            		</property>
            		<property name="patterns">
            			<list>
            				<value>.*fireEvent</value>
            			</list>
            		</property>
            	</bean>
            	<bean id="testFirerer"
            		class="org.springframework.aop.framework.ProxyFactoryBean">
            		<property name="proxyInterfaces"><value>se.iorlas.EventFirerer</value></property>
            		<property name="target"><ref local="testFirererTarget"/></property>
            		<property name="interceptorNames">
            			<list>
            				<value>fireAdvisor</value>
            			</list>
            		</property>
            	</bean>
            </beans>

            Comment


            • #7
              Further Explanation and Code

              Would you be able to supply the other example code that shows the entire integration of the example? I am fairly new to Spring and I am not putting the entire picture together correctly. If you were able to supply the code that acts against these event publisher/listeners, I may be able to further understand.

              This looks like an interesting option to solve some of our problems. Thanks .

              Comment


              • #8
                Just a quick tip BTW; never swallow exceptions . If it was my code I would let the exceptions propogate up. Knowing spring, they are probably all runtime anyway

                Comment

                Working...
                X