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?

No comments:

Post a Comment