Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Thursday, January 27, 2011

How to start JMS MessageListener latter than wicket application.

Issue

I'm using Spring for DI. I write as follows at applicationContext.xml for Wicket, Hibernate and JMS. The issue is caused by jmsListener start before wicketApplication.

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">  
  
    <!-- The wicket application bean -->
    <bean id="wicketApplication" class="my.project.MyWicketApplication" />

    <!-- specifies the place holder for resources of the project -->
    <bean id="placeholderConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreUnresolvablePlaceholders" value="false" />
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="false" />
        <property name="locations">
            <list>
                <value>classpath*:/application.properties</value>
            </list>
        </property>
    </bean>

    <!-- JMS beans -->
    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616"/>
    </bean>
 
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
    </bean>
 
    <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg index="0" value="ServerReceiveQueue"/>
    </bean>
 
    <bean id="jmsListener" class="my.project.jms.JmsListener"/>
  
    <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="destination"/>
        <property name="messageListener" ref="jmsListener"/>
    </bean>
  
    <!-- Data source, specifies the jdbc connection-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>${jdbc.driver}</value>
        </property>
        <property name="url">
            <value>${jdbc.url}</value>
        </property>
        <property name="username">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>
    </bean>

    <!-- Transaction manager -->
    <tx:annotation-driven transaction-manager="txManager" />
 
    <!-- setup transaction manager  -->
    <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>
 
    <!-- Hibernate session factory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.connection.pool_size">5</prop>
                <prop key="hibernate.show_sql">false</prop>
            </props>
        </property>
          
        <property name="packagesToScan">
            <list>
                <value>my.project.domain</value>
            </list>
        </property>
    </bean>

    <context:component-scan base-package="my.project" />

</beans>

The detail of issues..

"jmsListener" is instanciated before PropertyPlaceholderConfigurer start. When jmsListener start it receives some messages. And register some information at another class which has @Service annotation. But, after wicket application start, *maybe* Spring DI by PropertyPlaceholderConfigurer also instanciate several beans which have @Service annotation. And two instance which is instanciated not by PropertyPlaceholderConfigurer and by PropertyPlaceholderConfigurer seems to have different name. That means, even if the data was stored in a static object, it is not shared among them. So the wicket application can not access the data which jmsListener receives.

Solution

I delay the start of jmsListener. Set autoStartup to "false" and call start() inside WicketApplication#init().

<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="destination"/>
        <property name="messageListener" ref="jmsListener"/>
        <property name="autoStartup" value="false"/>
    </bean>

public class MyWicketApplication extends WebApplication
{     
    @SpringBean
    private DefaultMessageListenerContainer jmsContainer;
 
    @Override
    protected void init() {
        super.init();
        InjectorHolder.getInjector().inject(this);
        jmsContainer.start();
    }
}

Are there more smarter ways to solve the issue?