The Spring Framework provides a JMS abstraction called JmsTemplate. Unfortunately this class acts against a JMS provider in the same way as it acts against JMS in an application server. For example, to send a message, a new connection, session, and producer is created, the message is sent and everything is closed. This is done for every single message.
This behavior is necessary in a managed EJB (J2EE) environment, however, a J2EE application server pools its resources under the covers but if JmsTemplate is used against a plain JMS provider, it turns inefficient and slow.
Spring itself provides a class called SingleConnectionFactory which uses a single shared JMS connection but this does not avoid the creation of sessions, producers and consumers.
SwiftMQ provides an additional library called "springsupport.jar".
It is included in SwiftMQ releases 7.3.0 and up but can be used with any SwiftMQ release. The library uses a shared JMS connection and pools JMS sessions, producer and consumer objects.
| Date[dd.MM.yyyy] | Change |
|---|---|
| 04.06.2008 | Producer pools now use the destination name instead of the destination object to pool properly if the destination was dynamically resolved (same name but different object). |
| 16.07.2008 | Destroy method added to SingleSharedConnectionFactory. |
| 22.07.2008 | "clientId" property added to SingleSharedConnectionFactory. |
| 12.09.2008 | Support for unidentified message producer added (those which are created with "session.createProducer(null)"). |
| 27.11.2008 | "SwiftMQLauncher" bean added to start/shutdown SwiftMQ Router, e.g. for unit tests. |
The "SwiftMQLauncher" bean can be used to start a SwiftMQ Router out of Spring intra-VM. This is useful for unit tests. Example:
<bean id ="swiftmqLauncher" class="com.swiftmq.jms.springsupport.SwiftMQLauncher" init-method="startRouter" destroy-method="shutdownRouter">
<property name="workingDir" value="/Users/am/swiftmq_7_3_2/scripts/unix"/>
<property name="configFile" value="../../config/router1/routerconfig.xml"/>
<property name="registerShutdownHook" value="false"/>
</bean>
<bean id="jndiTemplate"
class="org.springframework.jndi.JndiTemplate" depends-on="swiftmqLauncher">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">com.swiftmq.jndi.InitialContextFactoryImpl</prop>
<prop key="java.naming.provider.url">smqp://intravm/timeout=10000</prop>
</props>
</property>
</bean>
<bean id="jmsConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean"
lazy-init="true">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>IVMConnectionFactory</value>
</property>
</bean>
...
If this bean is used to start SwiftMQ intra-VM, it is reasonable to use intra-VM connections, that is, use "intravm" as the host name for the SMQP-URL and lookup an intra-VM connection factory, e.g. "IVMConnectionFactory" as shown above.
The bean has 3 properties. Property "workingDir" must be set to the platform script directory and must be absolute. Property "configFile" is a relative path (relative to "workingDir") and points to the router's config file. Property "registerShutdownHook" specifies whether the router registers its own shutdown hook. This is not required if the destroy-method is defined because Spring itself calls it on program exit.
The init-method of the bean must be set to "startRouter" which starts the router. The destroy-method must be set to "shutdownRouter".
Keep in mind to depend your other beans on "SwiftMQLauncher" bean.
To use SingleSharedConnectionFactory with Spring, add it to your client's classpath and use class "com.swiftmq.jms.springsupport.SingleSharedConnectionFactory" as your connection factory as shown in this example:
<bean id="jmsConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean"
lazy-init="true">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>plainsocket@router1</value>
</property>
</bean>
<bean id="testqueue"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>testqueue@router1</value>
</property>
</bean>
<bean id ="singleSharedConnectionFactory"
class="com.swiftmq.jms.springsupport.SingleSharedConnectionFactory"
destroy-method="destroy" >
<property name="targetConnectionFactory" ref="jmsConnectionFactory"/>
<property name="poolExpiration" value="120000"/>
<property name="clientId" value="test"/>
</bean>
<bean id="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="singleSharedConnectionFactory"/>
</property>
<property name="defaultDestination">
<ref bean="testqueue"/>
</property>
</bean>
<bean id="springSender"
class="SpringSender">
<property name="jmsTemplate">
<ref bean="jmsTemplate"/>
</property>
</bean>
SingleSharedConnectionFactory uses a single shared JMS connection. This single connection provides pooling for JMS sessions where each JMS session pools producers and consumers.
The class provides 3 properties. One is "targetConnectionFactory" which needs to be set to a "real" connection factory obtained from SwiftMQ's JNDI. It is used to create the single shared JMS connection. The next is "poolExpiration" which defines the time in milliseconds after which a pooled object (session, producer, consumer) will expire when it has not being used within this time. So the pools grow and shrink dependent on the load. Default for "poolExpiration" is 60000 ms (1 minute). The last is "clientId" which is optional and can be set to the JMS client id.
To free all resources held from SingleSharedConnectionFactory define the destroy-method:
<bean id ="singleSharedConnectionFactory"
class="com.swiftmq.jms.springsupport.SingleSharedConnectionFactory"
destroy-method="destroy" >
<property name="targetConnectionFactory" ref="jmsConnectionFactory"/>
<property name="poolExpiration" value="120000"/>
<property name="clientId" value="test"/>
</bean>
SingleSharedConnectionFactory can also be used from plain JMS clients (without Spring). Just wrap your connection factory with SingleSharedConnectionFactory and you have pooling:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.swiftmq.jndi.InitialContextFactoryImpl");
env.put(Context.PROVIDER_URL,smqpURL);
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory connectionFactory = new SingleSharedConnectionFactory((QueueConnectionFactory)ctx.lookup(qcfName));
SingleSharedConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory and can be used for all JMS messaging domains.
To free all resources held from SingleSharedConnectionFactory call destroy:
((SingleSharedConnectionFactory)connectionFactory).destroy();
Debugging of the library can be enabled by setting the System Property "swiftmq.springsupport.debug" to true:
-Dswiftmq.springsupport.debug=true
Debug output goes to System.out.