Tag: JMS

Spring and JBoss AS7 JMS

In this post we are going to look at how we can set-up a client (build using Spring) that uses the JBoss JMS provider HornetQ remotely.

Remoting

Let us first look at how we can obtain objects remotely. In the Fun with JBoss post we have created an application that contains a stateless enterprise bean. This gives us a nice playground to call this stateless enterprise bean remotely. In order to do this we can use the following program

package test;

import model.entities.Person;
import model.logic.Company;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        properties.put(Context.PROVIDER_URL, "remote://192.168.1.150:4447");
        properties.put(Context.SECURITY_PRINCIPAL, "employee");
        properties.put(Context.SECURITY_CREDENTIALS, "welcome1");
        properties.put("jboss.naming.client.ejb.context", true);

        try {
            Context context = new InitialContext(properties);
            Company company = (Company) context.lookup("LoadTest6/Model/Company!model.logic.Company");
            company.insertPerson(createPerson());
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    private static Person createPerson() {
        Person person = new Person();
        Random generator = new Random();
        person.setNaam(Long.toString(Math.abs(generator.nextLong()), 36));
        person.setSofinummer(1);
        return person;
    }
}

There are some points worth mentioning:

  • We need jboss-client-7.1.0.Final.jar in the client’s class path. The jar file can be obtained from the $JBOSS_HOME/bin/client directory of a JBoss AS7.1 distribution.
  • An application user must be created. This can be accomplished by using add-user.sh, located in the $JBOSS_HOME/bin directory. Detailed steps can be found in the Building a Coherence Cluster with Multiple Application Servers post. This user is then used to set the principal and credentials of the naming context that will be set-up.
  • Only JNDI objects prepended by java:jboss/exported/ can be obtained remotely. In the example, in which we obtain a stateless enterprise bean remotely, the object is bound as follows: app-name/module-name/bean-name!bean-interface in which,
    • app-name: the name of the .ear (without the .ear suffix) or the application name configured via application.xml deployment descriptor. If the application is not packaged in an .ear then there will be no app-name part to the JNDI string.
    • module-name: the name of the .jar or .war (without the .jar/.war suffix) in which the bean is deployed or the module-name configured in web.xml/ejb-jar.xml of the deployment. The module name is mandatory in the JNDI string.
    • bean-name: the name of the bean which by default is the simple name of the bean implementation class. It can be overridden either by using the name attribute of the bean defining annotation (@Stateless(name = "Company") in this case) or the ejb-jar.xml deployment descriptor.
    • bean-interface: the fully qualified class name of the interface being exposed by the bean.

When this is all to confusing for comfort, the logging will help, as something like the following output will be present when an application is deployed that contains enterprise beans:

16:50:52,840 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-3) JNDI bindings for session bean named Company in deployment unit subdeployment "Model.jar" of deployment "LoadTest6.ear" are as follows:

java:global/LoadTest6/Model/Company!model.logic.Company
java:app/Model/Company!model.logic.Company
java:module/Company!model.logic.Company
java:jboss/exported/LoadTest6/Model/Company!model.logic.Company
java:global/LoadTest6/Model/Company
java:app/Model/Company
java:module/Company

Here, we see which objects are bound under the java:jboss/exported/ name-space that can be obtained remotely. In our example, we can use context.lookup("LoadTest6/Model/Company!model.logic.Company") to obtain an instance of the enterprise bean. Note that we must not include java:jboss/exported/, as the remote-naming project expects it to always be relative to the java:jboss/exported/ name-space.

Set-up a JBoss JMS Provider

In the domain configuration file, we have the following

<domain xmlns="urn:jboss:domain:1.1">

    <extensions>
		...
        <extension module="org.jboss.as.messaging"/>
		...
    </extensions>

	<profiles>
		<profile name="cluster">
			...
		    <subsystem xmlns="urn:jboss:domain:messaging:1.1">
                <hornetq-server>
                    <persistence-enabled>true</persistence-enabled>
                    <journal-file-size>102400</journal-file-size>
                    <journal-min-files>2</journal-min-files>

                    <connectors>
                        <netty-connector name="netty" socket-binding="messaging"/>
						...
                    </connectors>

                    <acceptors>
                        <netty-acceptor name="netty" socket-binding="messaging"/>
						...
                    </acceptors>

                    <security-settings>
                        <security-setting match="#">
                            <permission type="send" roles="guest"/>
                            <permission type="consume" roles="guest"/>
                            <permission type="createNonDurableQueue" roles="guest"/>
                            <permission type="deleteNonDurableQueue" roles="guest"/>
                        </security-setting>
                    </security-settings>

					...

                    <jms-connection-factories>
						...
                        <connection-factory name="RemoteConnectionFactory">
                            <connectors>
                                <connector-ref connector-name="netty"/>
                            </connectors>
                            <entries>
                                <entry name="RemoteConnectionFactory"/>
                                <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
                            </entries>
                        </connection-factory>
						...
                    </jms-connection-factories>

                    <jms-destinations>
                        <jms-queue name="testQueue">
                            <entry name="java:/queue/test"/>
                            <entry name="java:jboss/exported/jms/queue/test"/>
                        </jms-queue>
						...
                    </jms-destinations>
                </hornetq-server>
            </subsystem>
		</profile>
	</profiles>

	<interfaces>
        <interface name="management"/>
        <interface name="public"/>
        <interface name="unsecure"/>
    </interfaces>

    <socket-binding-groups>
        <socket-binding-group name="cluster-sockets" default-interface="public">
			...
            <socket-binding name="messaging" port="5445"/>
			...
        </socket-binding-group>
    </socket-binding-groups>

    <deployments>
        <deployment name="LoadTest6.ear" runtime-name="LoadTest6.ear">
            <content sha1="161f51dde7f085c822cc4c68b306d57f1bee902d"/>
        </deployment>
    </deployments>

    <server-groups>
        <server-group name="cluster-group" profile="cluster">
            <jvm name="default"/>
            <socket-binding-group ref="cluster-sockets"/>
            <deployments>
                <deployment name="LoadTest6.ear" runtime-name="LoadTest6.ear" enabled="false"/>
            </deployments>
        </server-group>
    </server-groups>

</domain>

The host configuration is set-up as follows:

<host name="jboss" xmlns="urn:jboss:domain:1.1">

    <management>
        <security-realms>
            <security-realm name="ManagementRealm">
                <authentication>
                    <properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
                </authentication>
            </security-realm>
            <security-realm name="ApplicationRealm">
                <authentication>
                    <properties path="application-users.properties" relative-to="jboss.domain.config.dir" />
                </authentication>
            </security-realm>
        </security-realms>
        <management-interfaces>
            <native-interface security-realm="ManagementRealm">
                <socket interface="management" port="${jboss.management.native.port:9999}"/>
            </native-interface>
            <http-interface security-realm="ManagementRealm">
                <socket interface="management" port="${jboss.management.http.port:9990}"/>
            </http-interface>
        </management-interfaces>
    </management>

    <domain-controller>
       <local/>
    </domain-controller>

    <interfaces>
        <interface name="management">
            <nic name="eth0"/>
        </interface>
        <interface name="public">
            <nic name="eth0"/>
        </interface>
        <interface name="unsecure">
            <inet-address value="127.0.0.1"/>
        </interface>
    </interfaces>

    <jvms>
    	<jvm name="default">
            <heap size="512m" max-size="512m"/>
            <permgen size="256m" max-size="256m"/>
            <jvm-options>
                <option value="-server"/>
                <option value="-XX:NewRatio=2"/>
                <option value="-XX:+UseParallelGC"/>
                <option value="-XX:ParallelGCThreads=2"/>
                <option value="-XX:MaxGCPauseMillis=200"/>
                <option value="-XX:GCTimeRatio=19"/>
                <option value="-XX:+UseParallelOldGC"/>
            </jvm-options>
        </jvm>
    </jvms>

    <servers>
        <server name="cluster-server1" group="cluster-group" auto-start="false">
        </server>
        <server name="cluster-server2" group="cluster-group" auto-start="false">
            <socket-bindings port-offset="1"/>
        </server>
    </servers>
</host>

Some points are worth mentioning in the above configuration:

  • In the host configuration, we have set-up two servers: cluster-server1 and cluster-server2. Note that these servers are bound to the cluster-group server group.
  • The server group is defined in the domain configuration and coupled to the cluster profile, the default JVM (defined in the host configuration), and the cluster-sockets socket binding group.
  • The default interface that cluster-sockets socket binding group uses, is the public interface. The public interface is configured to use the network interface card in this case eth0 (see the host configuration).
  • The messaging socket binding is part of the cluster-sockets socket binding group.
  • The netty connector is configured to use the messaging socket-binding.
  • The RemoteConnectionFactory is configured to use the netty connector so when a client looks up this connection factory it will receive this netty connector which will tell the client where to connect. A remark is in order: Once we get the JMS connection factory reference from the server by looking it up in JNDI and use it to create a connection, the final destination of the connection has nothing to do with the java.naming.provider.url (Context.PROVIDER_URL) that was used in the JNDI lookup. When looking up a connection factory in JNDI, the client gets a connector which is a simple stub telling the client where its connections should go. In this case the connections are bound to the network interface card and the port defined in the messaging socket binding.

Using JBoss JMS Remotely

To interact remotely with the JBoss JMS provider (HornetQ) we can use the following program to test (before we put everything into Spring)

package model.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;

public class JNDITest {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        properties.put(Context.PROVIDER_URL, "remote://192.168.1.150:4447");
        properties.put(Context.SECURITY_PRINCIPAL, "employee");
        properties.put(Context.SECURITY_CREDENTIALS, "welcome1");

        ConnectionFactory connectionFactory = null;
        Destination destination = null;

        try {
            Context context = new InitialContext(properties);
            connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
            destination = (Destination) context.lookup("jms/queue/test");

            System.out.println(connectionFactory);
            System.out.println(destination);

            sendMessage(connectionFactory, destination);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    private static void sendMessage(ConnectionFactory connectionFactory, Destination destination) {
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;

        try {
            connection = connectionFactory.createConnection("employee", "welcome1");
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            messageProducer = session.createProducer(destination);

            TextMessage text = session.createTextMessage();
            text.setText("Send some useful message");
            messageProducer.send(text);
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException f) {
                    f.printStackTrace();
                }
            }
        }

    }
}

One (or more) of the following exceptions can be encountered when running the program:

  1. Caused by: HornetQException[errorCode=2 message=Cannot connect to server(s). Tried with all available servers.]
  2. Caused by: HornetQException[errorCode=105 message=Unable to validate user: null]
  3. javax.jms.JMSSecurityException: User: employee doesn’t have permission=’SEND’ on address jms.queue.test

The first exception is usually due to a host mismatch. Note that we are using the netty connector for the RemoteConnectionFactory. The netty connector uses the following (NettyConnector.java) to obtain an address:

    remoteDestination = new InetSocketAddress(host, port);
    InetAddress inetAddress = ((InetSocketAddress) remoteDestination).getAddress();

So what address would that be? To check this we can use the following

import java.net.InetAddress;

public class WhatIsMyAddress {
    public static void main(String[] args) throws Exception {
        System.out.println(InetAddress.getLocalHost());
    }
}

When this program is run we get the following output:

[jboss@axis-into-ict temp]$ /home/jboss/jdk1.6.0_31/bin/java WhatIsMyAddress
axis-into-ict.nl/192.168.1.150

When we run the test to interact with the JBoss JMS provider remotely, we get the following

Aug 10, 2012 10:48:44 AM org.xnio.Xnio <clinit>
INFO: XNIO Version 3.0.3.GA
Aug 10, 2012 10:48:44 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.3.GA
Aug 10, 2012 10:48:44 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.2.GA
HornetQConnectionFactory [serverLocator=ServerLocatorImpl [initialConnectors=[org-hornetq-core-remoting-impl-netty-NettyConnectorFactory?port=5445&host=axis-into-ict-nl], discoveryGroupConfiguration=null], clientID=null, dupsOKBatchSize=1048576, transactionBatchSize=1048576, readOnly=false]
HornetQQueue[testQueue]

One important thing to note here, is that the host is set to axis-into-ict-nl instead of the expected axis-into-ict.nl, i.e., in the host the ‘.’ are replaced by ‘-’. In order to get the axis-into-ict-nl to be resolved to the right IP address we add this to the etc/hosts file on the client

192.168.1.150       axis-into-ict.nl axis-into-ict-nl

The second exception is because no user is provided in the creation of a connection, i.e., connection = connectionFactory.createConnection(); is used instead of connection = connectionFactory.createConnection("employee", "welcome1").

The third exception is because the provided user to create a connection does not have the right privileges. Note that in the JMS configuration the following is present

<security-settings>
    <security-setting match="#">
        <permission type="send" roles="guest"/>
        <permission type="consume" roles="guest"/>
        <permission type="createNonDurableQueue" roles="guest"/>
        <permission type="deleteNonDurableQueue" roles="guest"/>
    </security-setting>
</security-settings>

This means the user must have the guest role. To accomplish this edit the application-roles.properties file (located in the $JBOSS_HOME/domain/configuration directory) and add the guest role to the used user, for example,

employee=guest,EMPLOYEE
manager=MANAGER

Restart the server such that the changes are picked up.

Create the Spring Client

Now that we have everything in place we can set-up the Spring configuration, for example,

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="jnditemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">org.jboss.naming.remote.client.InitialContextFactory</prop>
                <prop key="java.naming.provider.url">remote://192.168.1.150:4447</prop>
                <prop key="java.naming.security.principal">employee</prop>
                <prop key="java.naming.security.credentials">welcome1</prop>
            </props>
        </property>
    </bean>
    <bean id="connectionfactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jnditemplate"/>
        <property name="jndiName" value="jms/RemoteConnectionFactory"/>
    </bean>
    <bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jnditemplate"/>
        <property name="jndiName" value="jms/queue/test"/>
    </bean>
    <bean id="credentialsconnectionfactory"
          class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
        <property name="targetConnectionFactory" ref="connectionfactory"/>
        <property name="username" value="employee"/>
        <property name="password" value="welcome1"/>
    </bean>
    <bean id="jmstemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="credentialsconnectionfactory"/>
        <property name="defaultDestination" ref="destination"/>
    </bean>
    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="credentialsconnectionfactory"/>
        <property name="destination" ref="destination"/>
        <property name="messageListener" ref="receiver"/>
    </bean>
    <bean id="sender" class="model.logic.JMSSender">
        <property name="jmsTemplate" ref="jmstemplate"/>
    </bean>
    <bean id="receiver" class="model.logic.JMSReceiver"/>
</beans>

in which, the referred classes JMSSender and JMSReceiver look as follows

package model.logic;

import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Random;

public class JMSSender {

    private Random generator = new Random();
    private JmsTemplate jmsTemplate;

    public JMSSender() {
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage() {
        getJmsTemplate().send(new MessageCreator(){
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage();
                message.setText(Long.toString(Math.abs(generator.nextLong()), 36));
                return message;
            }
        });
    }
}
package model.logic;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class JMSReceiver implements MessageListener {
    public JMSReceiver() {
    }

    public void onMessage(Message message) {
        TextMessage text = (TextMessage)message;
        try {
            message.acknowledge();
            System.out.println("received the following message: " + text.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

To test the set-up we can use

package model.test;

import model.logic.JMSSender;
import model.utils.SpringUtilities;

public class JMSTest {

    public static void main(String[] args) {
        JMSSender jmsSender = SpringUtilities.getJMSSender();

        while (true) {
            jmsSender.sendMessage();
            System.out.println("done sending message");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

When the test is run the following output is observed

Aug 10, 2012 11:19:41 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1ed2ae8: startup date [Fri Aug 10 11:19:41 CEST 2012]; root of context hierarchy
Aug 10, 2012 11:19:41 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Aug 10, 2012 11:19:41 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ef5502: defining beans [jnditemplate,connectionfactory,destination,credentialsconnectionfactory,jmstemplate,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Aug 10, 2012 11:19:41 AM org.xnio.Xnio <clinit>
INFO: XNIO Version 3.0.3.GA
Aug 10, 2012 11:19:41 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.3.GA
Aug 10, 2012 11:19:41 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.2.GA
Aug 10, 2012 11:19:42 AM org.jboss.naming.remote.protocol.v1.RemoteNamingStoreV1$MessageReceiver handleEnd
ERROR: Channel end notification received, closing channel Channel ID b3d15ef8 (outbound) of Remoting connection 009ffe3f to /192.168.1.150:4447
Aug 10, 2012 11:19:42 AM org.jboss.naming.remote.protocol.v1.RemoteNamingStoreV1$MessageReceiver handleEnd
ERROR: Channel end notification received, closing channel Channel ID c4cd8d30 (outbound) of Remoting connection 0032060c to /192.168.1.150:4447
Aug 10, 2012 11:19:42 AM org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup start
INFO: Starting beans in phase 2147483647
received the following message: Send some useful message
received the following message: q9em87lq59pb
done sending message
received the following message: 9l1e70x6e2yf
done sending message
received the following message: 1e0fqxcfs0os5
done sending message
received the following message: 8bcprios0t56
done sending message
received the following message: 1elnsn5m1sxf1
done sending message
received the following message: 5cnl5bt1ztp8
done sending message
received the following message: 1tnm5yf1dn6fx
done sending message
received the following message: j8efslaqykc6
done sending message
received the following message: t10r3842i2fi
done sending message
received the following message: j845q4yhyrgn
done sending message
received the following message: 4ky8mbz8h7kl
done sending message
received the following message: y5go5h1iwcl0
done sending message
received the following message: 5j6buhmnq9up
done sending message
received the following message: 1wdojt8n3kztv
done sending message
received the following message: 1k6p9352nwcgl
done sending message
received the following message: 1povempe0r3ju
done sending message

References

[1] EJB invocations from a remote client using JNDI.
[2] Remote EJB invocations via JNDI – EJB client API or remote-naming project.
[3] EJB invocations from a remote server instance.


WebLogic Messaging Bridge and Store-and-Forward Service

In this post we first set-up a WebLogic environment that uses the WebLogic Messaging Bridge to forward messages. Next, we use the store-and-forward service to do the same. We will use Spring to test the set-ups. We end the post by looking at some performance considerations.

Messaging bridge

The WebLogic messaging bridge is a forwarding mechanism that provides interoperability between WebLogic JMS implementations, and between JMS and other messaging products. Use the Messaging Bridge to integrate messaging applications between:

  • Any two implementations of WebLogic JMS, including those from separate releases of WebLogic Server
  • WebLogic JMS implementations that reside in separate WebLogic domains
  • WebLogic JMS and a third-party JMS product

A messaging bridge instance forwards messages between a pair of bridge source and target destinations. These destinations are mapped to a pair of bridge source and target destinations. The messaging bridge reads messages from the source bridge destination and forwards those messages to the target bridge destination. For WebLogic JMS and third-party JMS products, a messaging bridge communicates with source and target destinations using the Java EE Connector Architecture (JCA) resource adapters provided with WebLogic Server. Two resource adapters are provided, i.e.,

  • eis.jms.WLSConnectionFactoryJNDIXA – Provides transaction semantics. Used when the required quality of service (QOS) is exactly-once (the message will be delivered from the sending destination to the receiving destination using XA transactions so that the receiver gets exactly one copy of each message). This envelopes a received message and sends it within a user transaction (XA/JTA). The following requirements apply to use of this resource adapter:
    • Any WebLogic Server implementation being bridged must be release 7.0 or later
    • The source and target JMS connection factories must be configured to use the XAConnectionFactory
  • eis.jms.WLSConnectionFactoryJNDINoTX – Provides no transaction semantics. Used when the required QOS is atmost-once (Makes sure that the receiving destination receives only a single copy of the message or does not receive it at all) or duplicate-okay (the bridge acknowledges receiving the message from the source destination only after writing it to the target destination, because this is done outside the scope of a transaction, failures after writing the message to the target and before acknowledging the source can result in duplicate messages being delivered but should never result in a message being lost, this type of delivery is better known as at-least-once delivery). If the requested QOS is atmost-once, the resource adapter uses AUTO_ACKNOWLEDGE mode. If the requested QOS is duplicate-okay, CLIENT_ACKNOWLEDGE is used.

An instance of the messaging bridge maps each source destination with a target destination. Each destination is configured using one of the adapters. Each bridge instance is targeted to a specific WebLogic Server instance. If the source is a distributed destination, the JMS consumer load balancing rules will associate the bridge with a single destination. In this case, it is best to connect a separate bridge instance to each member of the source destination. This leads to a proliferation of bridge instances that must be reconfigured if the cluster membership changes. The messaging bridge must be used when storing and forwarding messages between JMS destinations where one or both destinations are either hosted by foreign JMS providers or running on older versions of WebLogic Server (prior 9.0) that do not support the store-and-forward service.

Configuration

To set-up a messaging bridge, we need to first set-up the JMS resources involved in the bridge, i.e., the connection factories and destinations. In the example below we use two WebLogic environments. On one WebLogic environment we create the following:

  • Persistent store targeted to the admin server
  • JMS Server targeted to the admin server
  • JMS Module targeted to the admin server with the following resources:
    • Connection Factory default targeting enabled, with JNDI jms/BridgeConnectionFactory (and is XA enabled)
    • Destination (Queue) targeted through a sub deployment to the JMS Server, with JNDI jms/BridgeCompanyQueue

On the other environment we create the following:

  • A cluster consisting of two managed servers
  • Two JMS servers each targeted to a migratable target belonging to a managed server in the cluster (note that a migratable target is automatically created when a managed server is clustered
  • Two persistent stores each targeted to a managed server in the cluster
  • JMS module targeted to the cluster with the following resources:
    • Connection Factory default targeting enabled, with JNDI jms/ConnectionFactory (and is XA enabled)
    • Uniform distributed destination (queue) targeted through a subdeployment to the JMS Servers, with JNDI jms/CompanyQueue

Next, we configure two JMS bridge destinations. One source destination from which the messaging bridge instance reads messages and one target destination where the messaging bridge instance sends the messages it receives from the source destination. In the admin console

  • Open the tree services, messaging, bridges
  • Click JMS bridge destinations, click new and enter the following parameters:
    • Name: SourceDestination
    • Adapter JNDI Name: eis.jms.ConnectionFactoryJNDINoTx
    • Adapter Classpath: When connecting to a third-party JMS product, the bridge destination must supply the product’s CLASSPATH in the WebLogic Server CLASSPATH.
    • Connection URL: t3://192.168.1.50:7001
    • Connection Factory JNDI Name: jms/BridgeConnectionFactory
    • Destination JNDI Name: jms/BridgeCompanyQueue
  • Click OK

The target destination is configured as

  • Open the tree services, messaging, bridges
  • Click JMS bridge destinations, click new and enter the following parameters:
    • Name: TargetDestination
    • Adapter JNDI Name: eis.jms.ConnectionFactoryJNDINoTx
    • Adapter Classpath: When connecting to a third-party JMS product, the bridge destination must supply the product’s CLASSPATH in the WebLogic Server CLASSPATH.
    • Connection URL: t3://192.168.1.50:9001,192.168.1.50:9002
    • Connection Factory JNDI Name: jms/ConnectionFactory
    • Destination JNDI Name: jms/CompanyQueue
  • Click OK

The actual messaging bridge can be created as follows:

  • Open the tree services, messaging, bridges
  • Click new and enter the following parameters:
    • Name: WebLogicToWebLogicBridge
    • Selector: can be left empty
    • Quality Of Service: atmost-once
    • Started: enabled
  • Click next and select the source destination in our case this is SourceDestination
  • Click next and select the messaging provider in our case this is WebLogic Server 7.0 or higher
  • Click next and select the target destination in our case this is TargetDestination
  • Click next and select the messaging provider for the target in our case this is WebLogic Server 7.0 or higher
  • Click next and target the messaging bridge to the server that holds to the source destination, which is our case is the adminserver
  • Click next and click finish

Note that after the creation the messaging bridge can be fine tuned.

WLST

The following script shows an example how the messaging bridge can be set-up using WLST

print 'CONNECT TO ADMIN SERVER';
connect('weblogic', 'magic12c', 't3://192.168.1.50:7001');

print 'START EDIT MODE';
edit();
startEdit();

print 'CREATE SOURCE JMS BRIDGE DESTINATION';
cmo.createJMSBridgeDestination('SourceDestination');
sourcedestination = cmo.lookupJMSBridgeDestination('SourceDestination');
sourcedestination.setClasspath('');
sourcedestination.setConnectionURL('t3://192.168.1.50:7001');
sourcedestination.setAdapterJNDIName('eis.jms.WLSConnectionFactoryJNDINoTX');
sourcedestination.setConnectionFactoryJNDIName('jms/BridgeConnectionFactory');
sourcedestination.setDestinationJNDIName('jms/BridgeCompanyQueue');

print 'CREATE TARGET JMS BRIDGE DESTINATION';
cmo.createJMSBridgeDestination('TargetDestination');
targetdestination = cmo.lookupJMSBridgeDestination('TargetDestination');
targetdestination.setClasspath('');
targetdestination.setConnectionURL('t3://192.168.1.50:9001,192.168.1.50:9002');
targetdestination.setAdapterJNDIName('eis.jms.WLSConnectionFactoryJNDINoTX');
targetdestination.setConnectionFactoryJNDIName('jms/ConnectionFactory');
targetdestination.setDestinationJNDIName('jms/CompanyQueue');

print 'CREATE MESSAGING BRIDGE';
cmo.createMessagingBridge('Bridge');
bridge = cmo.lookupMessagingBridge('Bridge');
adminserver = cmo.lookupServer('AdminServer');
targets = bridge.getTargets();
targets.append(adminserver);
bridge.setTargets(targets);
bridge.setSourceDestination(sourcedestination);
bridge.setTargetDestination(targetdestination);
bridge.setStarted(true);
bridge.setSelector('');
bridge.setQualityOfService('Atmost-once');

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

Test

To test if the set-up works we will use a Spring client. Make sure the wlclient and wljmsclient jar files are on the classpath. To set-up a JMS producer using Spring we can use the following configuration:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="sourceJndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                <prop key="java.naming.provider.url">t3://192.168.1.50:7001</prop>
            </props>
        </property>
    </bean>
    <bean id="sourceConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="sourceJndiTemplate"/>
        <property name="jndiName" value="jms/BridgeConnectionFactory"/>
    </bean>
    <bean id="sourceDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="sourceJndiTemplate"/>
        <property name="jndiName" value="jms/BridgeCompanyQueue"/>
    </bean>
    <bean id="sourceJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="sourceConnectionFactory"/>
        <property name="defaultDestination" ref="sourceDestination"/>
    </bean>
    <bean id="sender" class="model.logic.JMSSender">
        <property name="jmsTemplate" ref="sourceJmsTemplate"/>
    </bean>
</beans>

To send a message by using the configured JMSTemplate we can use the following class:

package model.logic;

import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Random;

public class JMSSender {

    private Random generator = new Random();
    private JmsTemplate jmsTemplate;

    public JMSSender() {
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage() {
        getJmsTemplate().send(new MessageCreator(){
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage();
                message.setText(Long.toString(Math.abs(generator.nextLong()), 36));
                return message;
            }
        });
    }
}

To run the class we can use:

package model.test;

import model.logic.JMSSender;
import model.utils.SpringUtilities;

public class JMSTest {

    public static void main(String[] args) {
        JMSSender jmsSender = SpringUtilities.getJMSSender();
        jmsSender.sendMessage();
    }
}

To consume a message we create another client using Spring’s SimpleMessageListenerContainer. We have the following configuration:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="targetJndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                <prop key="java.naming.provider.url">t3://192.168.1.50:9001,192.168.1.50:9002</prop>
            </props>
        </property>
    </bean>
    <bean id="targetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="targetJndiTemplate"/>
        <property name="jndiName" value="jms/ConnectionFactory"/>
    </bean>
    <bean id="targetDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="targetJndiTemplate"/>
        <property name="jndiName" value="jms/CompanyQueue"/>
    </bean>
    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="targetConnectionFactory"/>
        <property name="destination" ref="targetDestination"/>
        <property name="messageListener" ref="receiver"/>
    </bean>
    <bean id="receiver" class="model.logic.JMSReceiver"/>
</beans>

To receive a message asynchronously we use:

package model.logic;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class JMSReceiver implements MessageListener {
    public JMSReceiver() {
    }

    public void onMessage(Message message) {
        TextMessage text = (TextMessage)message;
        try {
            message.acknowledge();
            System.out.println("received the following message: " + text.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

Store-and-forward service

The Store-and-Forward (SAF) service enables WebLogic Server to deliver messages reliably between applications that are distributed across WebLogic Server instances. JMS modules utilize the SAF service to enable local JMS message producers to reliably send messages to remote queues or topics without worrying about the availability of the remote environment. If the destination is not available at the moment the messages are sent, either because of network problems or system failures, then the messages are saved on a local server instance, and are forwarded to the remote destination once it becomes available. The SAF Service should be used when forwarding JMS messages between WebLogic Server 9.x or later domains. The SAF service can deliver messages:

  • Between two stand-alone server instances
  • Between server instances in a cluster
  • Across two clusters in a domain
  • Across separate domains

When not to use the SAF service:

  • Forwarding messages to prior releases of WebLogic Server
  • Interoperating with third-party JMS products. For these tasks, we should use the WebLogic Messaging Bridge
  • When using temporary destinations with the JMSReplyTo field to return a response to a request

Additionally, when using JMS SAF, an application can only receive messages directly from a remote server, and only when the remote server is available. Both the store-and-forward service and the messaging bridge provide JMS applications a message forwarding agent-based technology. A thing to note is that the store-and-forward service uses an internal duplicate elimination algorithm that does not require XA transactions and thus will perform better for the exactly-once quality of service. The store-and-forward service uses agents to store and forward the messages between the sending and receiving sides. We must configure store-and-forward agents to support sending, receiving, or both depending on the set-up being used:

  • JMS sending – only a sending agent
  • JMS receiving – no agent needed
  • Web Services Reliable Messaging sending – sending and receiving agent
  • Web Services Reliable Messaging receiving – receiving agent

SAF agents are similar to JMS servers in that they have persistent stores, paging directories, and destinations, as well as quotas, thresholds, and other similar configuration parameters. The primary difference is that SAF agents only support imported destinations, a local representation of remote destinations to which messages are stored locally and then forwarded. SAF agents also support targeting to migratable targets to enable SAF agent service migration. Reliability in SAF is time-based in that the time-to-live attribute determines how long the agent will attempt to forward the message before expiring it. When setting a time-to-live on one of the SAF objects, a value of -1 means that the value is not set, 0 means that the message never expires, and a positive value defines the number of milliseconds after the message was created that the message will expire. If a message expires, SAF error handling provides four expiration policies from which to choose: Discard, Log, Redirect, and Always-forward. Always-forward ignores the time-to-live setting on the imported destinations and any message expiration time and forwards the message even after it has expired. Typically, this option would be used if the application had expiration policies set up on the remote destinations and we want the expired messages to be handled using these policies.

Configuration

To set-up a store-and-forward service we can use the following steps. First, we create a SAF agent:

  • In the admin console click services, messaging, store and forward agent
  • Click new and enter the following parameters:
    • name: SAFAgent
    • persistent store: AdminFileStore (or create a new one)
    • agent type: sending-only
  • Click next and enter the following parameters:
    • target: AdminServer (the same as the target of the persistent store)

Next, create a JMS module or use an existing one and create a subdeployment for the SAF agent

  • Click subdeployments, click new and enter the following parameters:
    • subdeployment name: SAFSubDeployment
  • Click next and select as target the created SAF agent
  • Click finish

Subsequently, we create a remote SAF context

  • Click the configuration tab of the JMS module
  • Click new, select remote SAF context, click next and enter the following parameters:
    • name: RemoteSAFContext
    • URL: t3://192.168.1.50:9001,192.168.1.50:9002
    • username: weblogic (admin username)
    • password: magic12c (admin password)
    • confirm password: magic12c
  • Click OK

Next, create SAF imported destinations

  • Click the configuration tab of the JMS module
  • Click new, select SAF imported destinations, click next and enter the following parameters:
    • name: SAFImportedDestinations
    • JNDI prefix: jms/ (this can be used for the local destination counterparts of remote destinations to which the SAF agent is connecting)
    • remote SAF context: RemoteSAFContext
  • Click next and click advanced targeting
  • Select SAFSubDeployment and click finish

In the last step we will add remote queues to the created SAF imported destination

  • Click the create SAF imported destination
  • Click the queues, configuration tab, click new and enter the following parameters:
    • name: SAFQueue
    • remote JNDI name: jms/CompanyQueue
  • Click OK
  • Click SAFQueue and set the local JNDI name to SAFCompanyQueue (note that we can now look up the local queue by using jms/SAFCompanyQueue, in which jms/ is the JNDI prefix we set earlier)
  • Save the configuration

To check if the configuration is correct check the server logging, something like the following should be present

####<Apr 16, 2012 10:43:49 AM CEST> <Info> <JMS> <axis-into-ict.nl> <AdminServer> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1334565829666> <BEA-040506> <The JMS store-and-forward (SAF) forwarder has successfully connected to the remote destination "t3://192.168.1.50:9001,192.168.1.50:9002/jms/CompanyQueue".>

When the servers to which the SAF agent is connecting are not present the following exception is observed

####<Apr 16, 2012 10:43:07 AM CEST> <Info> <JMS> <axis-into-ict.nl> <AdminServer> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1334565787556> <BEA-040507> <The JMS store-and-forward (SAF) forwarder failed to connect to the remote destination "t3://192.168.1.50:9001,192.168.1.50:9002/jms/CompanyQueue", because of javax.naming.CommunicationException [Root exception is java.net.ConnectException: t3://192.168.1.50:9001,192.168.1.50:9002: Destination unreachable; nested exception is:
	java.net.ConnectException: Connection refused; No available router to destination]
	at weblogic.jndi.internal.ExceptionTranslator.toNamingException(ExceptionTranslator.java:40)
	at weblogic.jndi.WLInitialContextFactoryDelegate.toNamingException(WLInitialContextFactoryDelegate.java:767)
	at weblogic.jndi.WLInitialContextFactoryDelegate.getInitialContext(WLInitialContextFactoryDelegate.java:366)
	at weblogic.jndi.Environment.getContext(Environment.java:315)
	at weblogic.jndi.Environment.getContext(Environment.java:285)
	at weblogic.jndi.Environment.createInitialContext(Environment.java:208)
	at weblogic.jndi.Environment.getInitialContext(Environment.java:192)
	at weblogic.jndi.Environment.getInitialContext(Environment.java:170)
	at weblogic.jndi.Environment.getContext(Environment.java:215)
	at weblogic.jms.forwarder.Forwarder.getInitialContext(Forwarder.java:428)
	at weblogic.jms.forwarder.Forwarder.connectTarget(Forwarder.java:447)
	at weblogic.jms.forwarder.Forwarder.reconnect(Forwarder.java:270)
	at weblogic.jms.forwarder.Forwarder.timerExpired(Forwarder.java:335)
	at weblogic.timers.internal.TimerImpl.run(TimerImpl.java:293)
	at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:545)
	at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
	at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)
Caused by: java.net.ConnectException: t3://192.168.1.50:9001,192.168.1.50:9002: Destination unreachable; nested exception is:
	java.net.ConnectException: Connection refused; No available router to destination
	at weblogic.rjvm.RJVMFinder.findOrCreateInternal(RJVMFinder.java:216)
	at weblogic.rjvm.RJVMFinder.findOrCreate(RJVMFinder.java:170)
	at weblogic.rjvm.ServerURL.findOrCreateRJVM(ServerURL.java:165)
	at weblogic.jndi.WLInitialContextFactoryDelegate$1.run(WLInitialContextFactoryDelegate.java:345)
	at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:363)
	at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:146)
	at weblogic.jndi.WLInitialContextFactoryDelegate.getInitialContext(WLInitialContextFactoryDelegate.java:340)
	... 14 more
Caused by: java.rmi.ConnectException: Destination unreachable; nested exception is:
	java.net.ConnectException: Connection refused; No available router to destination
	at weblogic.rjvm.ConnectionManager.bootstrap(ConnectionManager.java:470)
	at weblogic.rjvm.ConnectionManager.bootstrap(ConnectionManager.java:321)
	at weblogic.rjvm.RJVMManager.findOrCreateRemoteInternal(RJVMManager.java:260)
	at weblogic.rjvm.RJVMManager.findOrCreate(RJVMManager.java:197)
	at weblogic.rjvm.RJVMFinder.findOrCreateRemoteServer(RJVMFinder.java:238)
	at weblogic.rjvm.RJVMFinder.findOrCreateRemoteCluster(RJVMFinder.java:316)
	at weblogic.rjvm.RJVMFinder.findOrCreateInternal(RJVMFinder.java:205)
	... 20 more
.>

As can be seen from the previous log line (which in time is happening later), when the servers become available the SAF agent connects to the servers.

WLST

The following script shows an example how the save-and-forward service can be set-up using WLST

print 'CONNECT TO ADMIN SERVER';
connect('weblogic', 'magic12c', 't3://192.168.1.50:7001');

print 'START EDIT MODE';
edit();
startEdit();

print 'CREATE FILE STORE';
cmo.createFileStore('SAFFileStore');
saffilestore = cmo.lookupFileStore('SAFFileStore');
saffilestore.setDirectory('/home/oracle/weblogic12.1.1/configuration/applications/base_domain');
targets = saffilestore.getTargets();
adminserver = cmo.lookupServer('AdminServer');
targets.append(adminserver);
saffilestore.setTargets(targets);

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'CREATE SAF Agent';
cmo.createSAFAgent('SAFAgent');
safagent = cmo.lookupSAFAgent('SAFAgent');
safagent.setStore(saffilestore);
safagent.setTargets(targets);
safagent.setServiceType('Sending-only');

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'CREATE JMS MODULE';
cmo.createJMSSystemResource('jms-saf-module');
safmodule = cmo.lookupJMSSystemResource('jms-saf-module');
safmodule.setTargets(targets);
safmodule.createSubDeployment('SAFSubDeployment');
cd('/JMSSystemResources/jms-saf-module/SubDeployments/SAFSubDeployment');
set('Targets',jarray.array([ObjectName('com.bea:Name=SAFAgent,Type=SAFAgent')], ObjectName));
cd('/');

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'OBTAIN JMS RESOURCE';
resource = safmodule.getJMSResource();

print 'CREATE CONNECTION FACTORY';
resource.createConnectionFactory('SAFConnectionFactory');
connectionfactory = resource.lookupConnectionFactory('SAFConnectionFactory');
connectionfactory.setJNDIName('jms/SAFConnectionFactory');
connectionfactory.setDefaultTargetingEnabled(true);
connectionfactory.getTransactionParams().setTransactionTimeout(3600);
connectionfactory.getTransactionParams().setXAConnectionFactoryEnabled(true);
connectionfactory.getSecurityParams().setAttachJMSXUserId(false);
connectionfactory.getClientParams().setClientIdPolicy('Restricted');
connectionfactory.getClientParams().setSubscriptionSharingPolicy('Exclusive');
connectionfactory.getClientParams().setMessagesMaximum(10);

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'CREATE SAF REMOTE CONTEXT';
resource.createSAFRemoteContext('RemoteSAFContext');
safcontext = resource.lookupSAFRemoteContext('RemoteSAFContext');
safcontext.getSAFLoginContext().setLoginURL('t3://192.168.1.50:9001,192.168.1.50:9002');
safcontext.getSAFLoginContext().setUsername('weblogic');
safcontext.getSAFLoginContext().setPassword('magic12c');

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'CREATE SAF IMPORTED DESTINATIONS';
resource.createSAFImportedDestinations('SAFImportedDestinations');
importeddestinations = resource.lookupSAFImportedDestinations('SAFImportedDestinations');
importeddestinations.setJNDIPrefix('jms/');
importeddestinations.setSAFRemoteContext(safcontext);
importeddestinations.setSAFErrorHandling(None);
importeddestinations.setTimeToLiveDefault(0);
importeddestinations.setUseSAFTimeToLiveDefault(false);
importeddestinations.setSubDeploymentName('SAFSubDeployment');

print 'SAVE, ACTIVATE CHANGES AND START EDIT';
save();
activate(block='true');
startEdit();

print 'ADD QUEUE TO THE SAF IMPORTED DESTINATIONS';
importeddestinations.createSAFQueue('SAFQueue');
safqueue = importeddestinations.lookupSAFQueue('SAFQueue');
safqueue.setRemoteJNDIName('jms/CompanyQueue');
safqueue.setLocalJNDIName('SAFCompanyQueue');

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

Test

To test the set-up we will again use Spring. To set-up the sending end, we will use the following configuration

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="sourceJndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                <prop key="java.naming.provider.url">t3://192.168.1.50:7001</prop>
            </props>
        </property>
    </bean>
    <bean id="sourceConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="sourceJndiTemplate"/>
        <property name="jndiName" value="jms/SAFConnectionFactory"/>
    </bean>
    <bean id="sourceDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="sourceJndiTemplate"/>
        <property name="jndiName" value="jms/SAFCompanyQueue"/>
    </bean>
    <bean id="sourceJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="sourceConnectionFactory"/>
        <property name="defaultDestination" ref="sourceDestination"/>
    </bean>
    <bean id="timerListener" class="org.springframework.scheduling.commonj.ScheduledTimerListener">
        <property name="delay" value="10000"/>
        <property name="period" value="30000"/>
        <property name="runnable" ref="sender"/>
    </bean>
    <bean id="timerFactory" class="org.springframework.scheduling.commonj.TimerManagerFactoryBean">
        <property name="timerManagerName" value="java:comp/env/tm/default"/>
        <property name="resourceRef" value="true"/>
        <property name="scheduledTimerListeners">
            <list>
                <ref bean="timerListener"/>
            </list>
        </property>
    </bean>
    <bean id="sender" class="model.logic.JMSSender">
        <property name="jmsTemplate" ref="sourceJmsTemplate"/>
    </bean>
</beans>

in which the sender bean looks as follows

package model.logic;

import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Random;

public class JMSSender implements Runnable {

    private Random generator = new Random();
    private JmsTemplate jmsTemplate;

    public JMSSender() {
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage() {
        getJmsTemplate().send(new MessageCreator(){
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage();
                message.setText(Long.toString(Math.abs(generator.nextLong()), 36));
                return message;
            }
        });
    }

    public void run() {
        sendMessage();
    }
}

To set-up the receiving end, we will use the following configuration

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="targetJndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                <prop key="java.naming.provider.url">t3://192.168.1.50:9001,192.168.1.50:9002</prop>
            </props>
        </property>
    </bean>
    <bean id="targetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="targetJndiTemplate"/>
        <property name="jndiName" value="jms/ConnectionFactory"/>
    </bean>
    <bean id="targetDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="targetJndiTemplate"/>
        <property name="jndiName" value="jms/CompanyQueue"/>
    </bean>
    <bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
        <property name="workManagerName" value="java:comp/env/default"/>
        <property name="resourceRef" value="true"/>
    </bean>
    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="targetConnectionFactory"/>
        <property name="destination" ref="targetDestination"/>
        <property name="messageListener" ref="receiver"/>
        <property name="taskExecutor" ref="taskExecutor"/>
    </bean>
    <bean id="receiver" class="model.logic.JMSReceiver"/>
</beans>

in which the receiver bean looks as follows

package model.logic;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class JMSReceiver implements MessageListener {
    public JMSReceiver() {
    }

    public void onMessage(Message message) {
        TextMessage text = (TextMessage)message;
        try {
            message.acknowledge();
            System.out.println("received the following message: " + text.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

We deploy the sender to the AdminServer and the receiver to the cluster (the server set-up can be found here). To deploy the application we add a web.xml with the following contents

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
           version="3.0">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-config.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- default weblogic work manager -->
    <resource-ref>
        <res-ref-name>default</res-ref-name>
        <res-type>commonj.work.WorkManager</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>
    <!-- default weblogic timer manager -->
    <resource-ref>
        <res-ref-name>tm/default</res-ref-name>
        <res-type>commonj.timers.TimerManager</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>
    <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>test.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/testservlet</url-pattern>
    </servlet-mapping>
</web-app>

What happens when the applications run is that a JMS producer (sender) is connected to the SAF environment on the AdminServer. The JMS producer sends messages every 30 seconds, which is configured by using a commonj timer manager. The message send to the SAF queue is being forwarded to the jms/CompanyQueue (a distributed queue) to which a JMS consumer (receiver) is listening by using a SimpleMessageListenerContainer that is coupled to a commonj work manager in order to make sure the thread spawned by the listener is managed by the WebLogic Server. In the logging the following is observed

Logging server1 in the cluster
Apr 16, 2012 11:41:02 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Apr 16, 2012 11:41:02 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.XmlWebApplicationContext@1e3c6703: display name [Root WebApplicationContext]; startup date [Mon Apr 16 11:41:02 CEST 2012]; root of context hierarchy
Apr 16, 2012 11:41:03 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Apr 16, 2012 11:41:03 AM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.XmlWebApplicationContext@1e3c6703]: org.springframework.beans.factory.support.DefaultListableBeanFactory@1e4efd06
Apr 16, 2012 11:41:03 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1e4efd06: defining beans [sourceJndiTemplate,targetJndiTemplate,sourceConnectionFactory,sourceDestination,sourceJmsTemplate,targetConnectionFactory,targetDestination,taskExecutor,timerListener,timerFactory,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Apr 16, 2012 11:41:03 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 842 ms
received the following message: d8nv7v6vgm5j
received the following message: 1be4omcwwiy61
received the following message: 19y6wh44hora6
received the following message: ha5hp6gaw6ql
received the following message: xercuxsl5qwf
received the following message: 1vvx5lptldi8r
received the following message: 1cgsdl10dkokz
received the following message: qfmd6v14tbb6
received the following message: 53hsnn8dfmor
received the following message: 8uaecn6919mi
received the following message: 8i93wuglm4lj
received the following message: 11d5bkqc5vdnq
received the following message: wc9hpcqmzjj9
received the following message: 17zj64mnvj7d9
received the following message: 1dt5g68vv9d50
received the following message: 13ch0ee5ofrzb
received the following message: 121t0btwasrqs
received the following message: 1lr8qb6il7nac
received the following message: 1l6lm4lge9tjx
received the following message: 15bgb2y3qzxl8
received the following message: vlryb3s3cn1o
received the following message: 189ahnkk5me1n
received the following message: 1wlhom58c3u01

Logging server2 in the cluster
Apr 16, 2012 11:41:02 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Apr 16, 2012 11:41:02 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.XmlWebApplicationContext@1e9e9ebc: display name [Root WebApplicationContext]; startup date [Mon Apr 16 11:41:02 CEST 2012]; root of context hierarchy
Apr 16, 2012 11:41:03 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Apr 16, 2012 11:41:03 AM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.XmlWebApplicationContext@1e9e9ebc]: org.springframework.beans.factory.support.DefaultListableBeanFactory@1ec29f17
Apr 16, 2012 11:41:03 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ec29f17: defining beans [sourceJndiTemplate,targetJndiTemplate,sourceConnectionFactory,sourceDestination,sourceJmsTemplate,targetConnectionFactory,targetDestination,taskExecutor,timerListener,timerFactory,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Apr 16, 2012 11:41:03 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 686 ms
received the following message: 1uwivfp2ngq1t
received the following message: bo4z7iakwqqv
received the following message: 1sdrelhucxxed
received the following message: 99xztxrc3f9f
received the following message: 1u8ej4ls0avqt
received the following message: hhkpei2bqyaw
received the following message: 2nid8k122rnd
received the following message: m2n72h6ajiao
received the following message: 1wpp89uiqttrw
received the following message: 18ecmmhgmmheu
received the following message: 1neja8ptey39a
received the following message: 1iu5em4cknpaa
received the following message: p89q16c2npbp
received the following message: 13gx5jqyzh57n
received the following message: 6lcjw5vjpttp
received the following message: 74wvvr4ey35s
received the following message: 1mcnjxzy2xp6f
received the following message: 12d6ybdpmi2wp

The extra messages in server1 are there because when running Spring as a client instead of being deployed to the WebLogic cluster, the receiver is only registered to one queue in the distributed queue. To test the set-up (without the timer manager and work manager configuration and) without deploying the application to WebLogic we can use

package model.test;

import model.logic.JMSSender;
import model.utils.SpringUtilities;

public class JMSTest {

    public static void main(String[] args) {
        JMSSender jmsSender = SpringUtilities.getJMSSender();

        while (true) {
            jmsSender.sendMessage();
            System.out.println("done sending message");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Performance considerations

The following documents provide information on various methods to improve performance:

Another tuning parameter to consider is the chunk size. A chunk is a unit of memory that the WebLogic Server network layer, both on the client and server side, uses to read data from and write data to sockets. To reduce memory allocation costs, a server instance maintains a pool of these chunks. For applications that handle large amounts of data per request, increasing the value on both the client and server sides can boost performance. The default chunk size is about 4K. Use the following properties to tune the chunk size and the chunk pool size:

  • weblogic.Chunksize – sets the size of a chunk (in bytes). The primary situation in which this may need to be increased is if request sizes are large. It should be set to values that are multiples of the network’s maximum transfer unit (MTU), after subtracting from the value any Ethernet or TCP header sizes. Set this parameter to the same value on the client and server.
  • weblogic.utils.io.chunkpoolsize – sets the maximum size of the chunk pool. The default value is 2048. The value may need to be increased if the server starts to allocate and discard chunks in steady state. To determine if the value needs to be increased, monitor the CPU profile or use a memory/ heap profiler for call stacks invoking the constructor weblogic.utils.io.Chunk.
  • weblogic.PartitionSize – sets the number of pool partitions used (default is 4). The chunk pool can be a source of significant lock contention as each request to access to the pool must be synchronized. Partitioning the thread pool spreads the potential for contention over more than one partition.

For example, we can set the chunksize by using -Dweblogic.Chunksize=65536 on the command-line.

References

[1] Configuring and Managing JMS for WebLogic Server.
[2] Configuring and Managing the Messaging Bridge for WebLogic Server.
[3] Configuring and Managing Store-and-Forward for WebLogic Server.
[4] Performance and Tuning for WebLogic Server.


Spring and GlassFish JMS

In this post we set-up a GlassFish Server environment that includes a JMS configuration that will be used by a Spring client. First, we look at some considerations when developing a client that uses JMS. Next, we look at the classloading, i.e., the classes needed in order for Spring to communicate with GlassFish and obtain JMS resources from JNDI. Finally, we show the configuration needed in Spring and test the set-up.

Some notes up front

The Java Messaging Specification was developed to abstract access to message-oriented middleware systems. A client that writes JMS code should be portable to any provider that implements this specification. If code portability is important, be sure to do the following in developing clients:

  • Make sure the code does not depend on extensions or features that are specific to Message Queue.
  • Look up, using JNDI, (rather than instantiate) administered objects for connection factories and destinations.

Another point to consider when developing a client is to use threads effectively, i.e., we need to balance performance, throughput, and resource needs. To do this, we need to understand JMS restrictions on thread usage, what threads Message Queue allocates for itself, and the architecture of the applications. The Java Messaging Specification mandates that a session not be operated on by more than one thread at a time. This leads to the following restrictions:

  • A session may not have an asynchronous consumer and a synchronous consumer.
  • A session that has an asynchronous consumer can only produce messages from within the onMessage() method (the message listener). The only call that we can make outside the message listener is to close the session.
  • A session may include any number of synchronous consumers, any number of producers, and any combination of the two. That is, the single-thread requirement cannot be violated by these combinations. However, performance may suffer.

The system does not enforce the requirement that a session be single threaded. If the client application violates this requirement, we will get a JMSIllegalState exception or unexpected results. When the Message Queue client runtime creates a connection, it creates two threads: one for consuming messages from the socket, and one to manage the flow of messages for the connection. In addition, the client runtime creates a thread for each client session. Thus, at a minimum, for a connection using one session, three threads are created. For a connection using three sessions, five threads are created, and so on. Managing threads in a JMS application often involves trade-offs between performance and throughput. Weigh the following considerations when dealing with threading issues. When we create several asynchronous message consumers in the same session, messages are delivered serially by the session thread to these consumers. Sharing a session among several message consumers might starve some consumers of messages while inundating other consumers. If the message rate across these consumers is high enough to cause an imbalance, we might want to separate the consumers into different sessions. To determine whether message flow is unbalanced, we can monitor destinations to see the rate of messages coming in. We can reduce the number of threads allocated to the client application by using fewer connections and fewer sessions. However, doing this might slow the application’s throughput. We might be able to use certain JVM runtime options to improve thread memory usage and performance.

A client application running in a JVM needs enough memory to accommodate messages that flow in from the network as well as messages the client creates. If the client gets OutOfMemoryError errors, chances are that not enough memory was provided to handle the size or the number of messages being consumed or produced. The client might need more than the default JVM heap space. Consider the following guidelines:

  • Evaluate the normal and peak system memory footprints when sizing heap space.
  • Adjust the heap size settings accordingly (the best size for the heap space depends on both the operating system and the JDK release).

In general, for better manageability, we can break large messages into smaller parts, and use sequencing to ensure that the partial messages sent are concatenated properly. We can also use a Message Queue JMS feature to compress the body of a message. Compression only affects the message body; the message header and properties are not compressed. Although message compression has been added to improve performance, such benefit is not guaranteed. Benefits vary with the size and format of messages, the number of consumers, network bandwidth, and CPU performance. For example, the cost of compression and decompression might be higher than the time saved in sending and receiving a compressed message. This is especially true when sending small messages in a high-speed network. On the other hand, applications that publish large messages to many consumers or who publish in a slow network environment, might improve system performance by compressing messages.

Persistent messages guarantee message delivery in case of broker failure. The broker stores these message in a persistent store until all intended consumers acknowledge that they have consumed the message. Broker processing of persistent messages is slower than for nonpersistent messages for the following reasons:

  • A broker must reliably store a persistent message so that it will not be lost should the broker fail.
  • The broker must confirm receipt of each persistent message it receives. Delivery to the broker is guaranteed once the method producing the message returns without an exception.
  • Depending on the client acknowledgment mode, the broker might need to confirm a consuming client’s acknowledgment of a persistent message.

A transaction guarantees that all messages produced in a transacted session and all messages consumed in a transacted session will be either processed or not processed (rolled back) as a unit. A message produced or acknowledged in a transacted session is slower than in a non-transacted session for the following reasons:

  • Additional information must be stored with each produced message.
  • In some situations, messages in a transaction are stored when normally they would not be. For example, a persistent message delivered to a topic destination with no subscriptions would normally be deleted, however, at the time the transaction is begun, information about subscriptions is not available.
  • Information on the consumption and acknowledgment of messages within a transaction must be stored and processed when the transaction is committed.

Other than using transactions, we can ensure reliable delivery by having the client acknowledge receiving a message. The messaging provider can sort messages according to criteria specified in the message selector associated with a consumer and deliver to that consumer only those messages whose property value matches the message selector. Creating consumers with selectors lowers performance (as compared to using multiple destinations) because additional processing is required to handle each message. When a selector is used, it must be parsed so that it can be matched against future messages. Additionally, the message properties of each message must be retrieved and compared against the selector as each message is routed. However, using selectors provides more flexibility in a messaging application and may lower resource requirements at the expense of speed.

Message size affects performance because more data must be passed from producing client to broker and from broker to consuming client, and because for persistent messages a larger message must be stored. However, by batching smaller messages into a single message, the routing and processing of individual messages can be minimized, providing an overall performance gain. In this case, information about the state of individual messages is lost. JMS supports five message body types, shown below roughly in the order of complexity:

  • Bytes: Contains a set of bytes in a format determined by the application.
  • Text: Is a java.lang.String.
  • Stream: Contains a stream of Java primitive values.
  • Map: Contains a set of name-and-value pairs.
  • Object: Contains a Java serialized object.

While, in general, the message type is dictated by the needs of an application, the more complicated types (map and object) carry a performance cost – the expense of serializing and deserializing the data. The performance cost depends on how simple or how complicated the data is.

Developing the JMS client with Spring

When using GlassFish JMS in a Spring client we need the right classes on the class path. The gf-client.jar contains all the necessary references, i.e., the MANIFEST.MF contained in the jar file looks as follows

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: Apache Maven
Archiver-Version: Plexus Archiver
Built-By: java_re
Build-Jdk: 1.6.0_10
Package: org.glassfish.appclient.client.acc
Main-Class: org.glassfish.appclient.client.AppClientFacade
GlassFish-ServerExcluded: true
PreMain-Class: org.glassfish.appclient.client.acc.agent.AppClientContainerAgent
Class-Path: ../modules/jtype.jar ../modules/tools.jar ../modules/glassfish-corba-asm.jar ../modules/glassfish-corba-codegen.jar ../modules/glassfish-corba-csiv2-idl.jar ../modules/glassfish-corba-internal-api.jar ../modules/glassfish-corba-newtimer.jar ../modules/glassfish-corba-omgapi.jar ../modules/glassfish-corba-orb.jar ../modules/glassfish -corba-orbgeneric.jar ../modules/auto-depends.jar ../modules/config.jar ../modules/config-types.jar ../modules/hk2.jar ../modules/hk2-core.jar ../modules/osgi-adapter.jar ../modules/grizzly-comet.jar ../modules/grizzly-config.jar ../modules/grizzly-framework.jar ../modules/grizzly-http.jar ../modules/grizzly-http-servlet.jar ../modules/grizzly-lzma.jar ../modules/grizzly-portunif.jar ../modules/grizzly-rcm.jar ../modules/grizzly-utils.jar ../modules/grizzly-websockets.jar ../modules/javax.mail.jar ../modules/pkg-client.jar ../modules/jaxb-osgi.jar ../modules/activation.jar ../modules/el-api.jar ../modules/jaxr-api-osgi.jar ../modules/jaxrpc-api-osgi.jar ../modules/endorsed/jaxb-api-osgi.jar ../modules/stax-api.jar ../modules/junit.jar ../modules/stax2-api.jar ../modules/woodstox-core-asl.jar ../modules/javax.persistence.jar ../modules/org.eclipse.persistence.antlr.jar ../modules/org.eclipse.persistence.asm.jar ../modules/org.eclipse.persistence.core.jar ../modules/org.eclipse.persistence.jpa.jar ../modules/org.eclipse.persistence.jpa.modelgen.jar ../modules/org.eclipse.persistence.oracle.jar ../modules/endorsed/javax.annotation.jar ../modules/javax.ejb.jar ../modules/javax.enterprise.deploy.jar ../modules/javax.jms.jar ../modules/javax.management.j2ee.jar ../modules/javax.resource.jar ../modules/javax.security.auth.message.jar ../modules/javax.security.jacc.jar ../modules/javax.servlet.jar ../modules/javax.servlet.jsp.jar ../modules/javax.transaction.jar ../modules/simple-glassfish-api.jar ../modules/admin-core.jar ../modules/admin-util.jar ../modules/config-api.jar ../modules/monitoring-core.jar ../modules/acc-config.jar ../modules/gf-client-module.jar ../modules/gms-bootstrap.jar ../modules/amx-core.jar ../modules/amx-j2ee.jar ../modules/annotation-framework.jar ../modules/common-util.jar ../modules/container-common.jar ../modules/glassfish-api.jar ../modules/glassfish-ee-api.jar ../modules/glassfish-naming.jar ../modules/internal-api.jar ../modules/scattered-archive-api.jar ../modules/stats77.jar ../modules/connectors-inbound-runtime.jar ../modules/connectors-internal-api.jar ../modules/connectors-runtime.jar ../modules/work-management.jar ../modules/glassfish.jar ../modules/kernel.jar ../modules/logging.jar ../modules/deployment-common.jar ../modules/deployment-javaee-core.jar ../modules/dol.jar ../modules/ejb-container.jar ../modules/ejb-internal-api.jar ../modules/ldapbp-repackaged.jar ../modules/libpam4j-repackaged.jar ../modules/management-api.jar ../modules/flashlight-framework.jar ../modules/gmbal.jar ../modules/ha-api.jar ../modules/class-model.jar ../modules/asm-a ll-repackaged.jar ../modules/bean-validator.jar ../modules/jms-core.jar ../modules/endorsed/webservices-api-osgi.jar ../modules/webservices-extra-jdk-packages.jar ../modules/webservices-osgi.jar ../modules/orb-connector.jar ../modules/orb-iiop.jar ../modules/eclipselink-wrapper.pom ../modules/jpa-connector.jar ../modules/persistence-common.jar ../modules/cmp-internal-api.jar ../modules/appclient.security.jar ../modules/ejb.security.jar ../modules/jaspic.provider.framework.jar ../modules/security.jar ../modules/ssl-impl.jar ../modules/websecurity.jar ../modules/webservices.security.jar ../modules/jta.jar ../modules/jts.jar ../modules/transaction-internal-api.jar ../modules/el-impl.jar ../modules/jsp-impl.jar ../modules/war-util.jar ../modules/web-cli.jar ../modules/web-core.jar ../modules/web-embed-api.jar ../modules/web-glue.jar ../modules/web-gui-plugin-common.jar ../modules/web-naming.jar ../modules/jsr109-impl.jar ../modules/mimepull.jar ../modules/tiger-types.jar ../modules/shoal-gms-api.jar ../../mq/lib/imq.jar ../ ../mq/lib/imqadmin.jar ../../mq/lib/imqutil.jar ../../mq/lib/fscontext.jar ../lib/install/applications/jmsra/imqjmsra.jar ../lib/install/applications/__ds_jdbc_ra/__ds_jdbc_ra.jar ../lib/install/applications/__cp_jdbc_ra/__cp_jdbc_ra.jar ../lib/install/applications/__xa_jdbc_ra/__xa_jdbc_ra.jar ../lib/install/applications/__dm_jdbc_ra/__dm_jdbc_ra.jar ../../javadb/lib/derby.jar ../../javadb/lib/derbyclient.jar ../../javadb/lib/derbynet.jar ../../javadb/lib/derbytools.jar ../../javadb/lib/derbyrun.jar ../lib/install/applications/jaxr-ra/jaxr-ra.jar ../modules/aixporting-repackaged.jar
GlassFish-Conditional-Additions: ../modules/aixporting-repackaged.jar

which is of course quite a lot to have on the classpath when we just want to use JMS. Note that it also means we have to install GlassFish on the client. So naturally you start to look for alternative ways. To see if we can obtain an object from a GlassFish server by using JNDI, we can create a little test class, for example,

package model.test;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;

public class JMSTest {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
        props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
        props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
        props.setProperty("org.omg.CORBA.ORBInitialHost", "192.168.1.50");
        props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");

        try {
            Context context = new InitialContext(props);
            System.out.println(context);
            System.out.println(context.lookup("jms/ConnectionFactory"));
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
}

When running this class we of course miss classes on the class path. To find which jar contains the missing class we can use

#!/bin/sh

PATH=${PATH}:/home/oracle/jdk1.6.0_31/bin
export PATH

LOOK_FOR="org/glassfish/api/admin/ProcessEnvironment$ProcessType"
export LOOK_FOR

for i in `find /home/oracle/glassfish3/glassfish/modules -name "*jar"`
do
	jar tvf $i | grep $LOOK_FOR > /dev/null
	if [ $? == 0 ]
	then
		echo "==> Found \"$LOOK_FOR\" in $i"
	fi
done

Here the LOOK_FOR variable is set to the value for the missing class and the first parameter in the find command is the path in which to look. After a while we get to a collection of these classes

auto-depends.jar
common-util.jar
glassfish-api.jar
glassfish-corba-internal-api.jar
glassfish-naming.jar
hk2-core.jar
internal-api.jar

and run into the following exception

Caused by: javax.naming.NamingException: Unable to acquire SerialContextProvider for SerialContext[myEnv={java.naming.provider.url=iiop://192.168.1.50:3700, java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory, java.naming.factory.url.pkgs=com.sun.enterprise.naming, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl} [Root exception is java.lang.NullPointerException]
	at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:352)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:504)
	... 8 more
Caused by: java.lang.NullPointerException
	at com.sun.enterprise.naming.impl.SerialContext.getORB(SerialContext.java:365)
	at com.sun.enterprise.naming.impl.SerialContext.getProviderCacheKey(SerialContext.java:372)
	at com.sun.enterprise.naming.impl.SerialContext.getRemoteProvider(SerialContext.java:402)
	at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:347)
	... 9 more

Now what? You go for the gf-client.jar and install GlassFish on the client. Not the nicest of solutions but one that works. The gf-client.jar file is located in the ${GLASS_HOME}/glassfish/lib directory. With gf-client.jar on the class path we get the following output when running the test

javax.naming.InitialContext@682bc3f5
Apr 12, 2012 4:59:02 PM org.hibernate.validator.util.Version <clinit>
INFO: Hibernate Validator 4.1.0.Final
Apr 12, 2012 4:59:02 PM org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
INFO: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
Apr 12, 2012 4:59:02 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter: Version:  4.5.1  (Build 3-b) Compile:  Tue Jun 21 16:31:32 PDT 2011
Apr 12, 2012 4:59:02 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter starting: broker is REMOTE, connection mode is TCP
Apr 12, 2012 4:59:02 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter Started:REMOTE
com.sun.messaging.jms.ra.ConnectionFactoryAdapter@61672c01

Next we need to configure Spring

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">com.sun.enterprise.naming.SerialInitContextFactory</prop>
                <prop key="java.naming.factory.url.pkgs">com.sun.enterprise.naming</prop>
                <prop key="java.naming.factory.state">com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl</prop>
                <prop key="org.omg.CORBA.ORBInitialHost">192.168.1.50</prop>
                <prop key="org.omg.CORBA.ORBInitialPort">3700</prop>
            </props>
        </property>
    </bean>
    <bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="jms/ConnectionFactory"/>
    </bean>
    <bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="jms/CompanyQueue"/>
    </bean>
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="defaultDestination" ref="destination"/>
    </bean>
    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="destination"/>
        <property name="messageListener" ref="receiver"/>
    </bean>
    <bean id="sender" class="model.logic.JMSSender">
        <property name="jmsTemplate" ref="jmsTemplate"/>
    </bean>
    <bean id="receiver" class="model.logic.JMSReceiver"/>
</beans>

The classes JMSSender and JMSReceiver can be found in the post WebLogic JMS Clustering and Spring. The post Fun with GlassFish shows the steps involved in how to create the JMS resources (connection factory and queue). To test the set-up we can use

package model.test;

import model.logic.JMSSender;
import model.utils.SpringUtilities;

public class JMSTest {

    public static void main(String[] args) {
        JMSSender jmsSender = SpringUtilities.getJMSSender();
        jmsSender.sendMessage();
        System.out.println("done sending message");
    }
}

which leads to the following output

Apr 12, 2012 5:30:45 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@78b5f53a: display name [org.springframework.context.support.ClassPathXmlApplicationContext@78b5f53a]; startup date [Thu Apr 12 17:30:45 CEST 2012]; root of context hierarchy
Apr 12, 2012 5:30:45 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Apr 12, 2012 5:30:45 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@78b5f53a]: org.springframework.beans.factory.support.DefaultListableBeanFactory@21b64e6a
Apr 12, 2012 5:30:45 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@21b64e6a: defining beans [jndiTemplate,connectionFactory,destination,jmsTemplate,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Apr 12, 2012 5:30:49 PM org.hibernate.validator.util.Version <clinit>
INFO: Hibernate Validator 4.1.0.Final
Apr 12, 2012 5:30:49 PM org.hibernate.validator.engine.resolver.DefaultTraversableResolver detectJPA
INFO: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
Apr 12, 2012 5:30:49 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter: Version:  4.5.1  (Build 3-b) Compile:  Tue Jun 21 16:31:32 PDT 2011
Apr 12, 2012 5:30:49 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter starting: broker is REMOTE, connection mode is TCP
Apr 12, 2012 5:30:49 PM com.sun.messaging.jms.ra.ResourceAdapter start
INFO: MQJMSRA_RA1101: GlassFish MQ JMS Resource Adapter Started:REMOTE
received the following message: 8dxgbdo4u4ee
done sending message

References

[1] GlassFish Server Documentation.


WebLogic JMS Clustering and Spring

In this post we set-up a WebLogic cluster that includes a JMS environment, which will be used by Spring. A distributed queue will be used in order to load balance the JMS message processing.

Create domain

To create our domain, we are going to use WLST. The following script, creates the domain in production mode, disables hostname verification, creates two managed server that are clustered and creates the artifacts needed to run the application.

beahome = '/home/oracle/weblogic12.1.1';
pathseparator = '/';
adminusername = 'weblogic';
adminpassword = 'magic12c';
adminservername='AdminServer';
adminserverurl='t3://axis-into-ict.nl:7001';
domainname = 'base_domain';
domaindirectory = beahome + pathseparator + 'configuration' + pathseparator + 'domains' + pathseparator + domainname;
domaintemplate = beahome + pathseparator + 'wlserver_12.1' + pathseparator + 'common' + pathseparator + 'templates' + pathseparator + 'domains' + pathseparator + 'wls.jar';
jvmdirectory = '/home/oracle/jrockit-jdk1.6.0_29-R28.2.2-4.1.0';

print 'CREATE DOMAIN';
readTemplate(domaintemplate);
setOption('DomainName', domainname);
setOption('OverwriteDomain', 'true');
setOption('ServerStartMode', 'prod');
cd('/Security/' + domainname + '/User/weblogic');
cmo.setName(adminusername);
cmo.setUserPassword(adminpassword);
cd('/');
writeDomain(domaindirectory);

print 'START ADMIN SERVER';
startServer(adminservername, domainname, adminserverurl, adminusername, adminpassword, domaindirectory);

print 'CONNECT TO ADMIN SERVER';
connect(adminusername, adminpassword, adminserverurl);

print 'START EDIT MODE';
edit();
startEdit();

print 'DISABLE HOSTNAME VERIFICATION';
cd('/Servers/' + adminservername + '/SSL/' + adminservername);
cmo.setHostnameVerificationIgnored(true);
cmo.setHostnameVerifier(None);
cmo.setTwoWaySSLEnabled(false);
cmo.setClientCertificateEnforced(false);
cd('/');

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

print 'SHUTDOWN THE ADMIN SERVER';
shutdown(block='true');

print 'START ADMIN SERVER';
startServer(adminservername, domainname, adminserverurl, adminusername, adminpassword, domaindirectory);

print 'CONNECT TO ADMIN SERVER';
connect(adminusername, adminpassword, adminserverurl);

print 'START EDIT MODE';
edit();
startEdit();

print 'CREATE MACHINE: machine1';
machine1 = cmo.createUnixMachine('machine1');
machine1.setPostBindUIDEnabled(true);
machine1.setPostBindUID('oracle');
machine1.getNodeManager().setListenAddress('axis-into-ict.nl');
machine1.getNodeManager().setNMType('ssl');

print 'CREATE CLUSTER: CLUSTER';
cluster = cmo.createCluster('cluster');
cluster.setClusterMessagingMode('unicast');

print 'CREATE MANAGED SERVER: server1';
server1 = cmo.createServer('server1');
server1.setListenPort(9001);
server1.setListenAddress('axis-into-ict.nl');
server1.setAutoRestart(true);
server1.setAutoKillIfFailed(true);
server1.setRestartMax(2);
server1.setRestartDelaySeconds(10);
server1.getServerStart().setJavaHome(jvmdirectory);
server1.getServerStart().setJavaVendor('Oracle');
server1.getServerStart().setArguments('-jrockit -Xms512m -Xmx512m -Xgc:throughput');

print 'CREATE MANAGED SERVER: server2';
server2 = cmo.createServer('server2');
server2.setListenPort(9002);
server2.setListenAddress('axis-into-ict.nl');
server2.setAutoRestart(true);
server2.setAutoKillIfFailed(true);
server2.setRestartMax(2);
server2.setRestartDelaySeconds(10);
server2.getServerStart().setJavaHome(jvmdirectory);
server2.getServerStart().setJavaVendor('Oracle');
server2.getServerStart().setArguments('-jrockit -Xms512m -Xmx512m -Xgc:throughput');

print 'ADD MANAGED SERVERS TO CLUSTER';
server1.setCluster(cluster);
server2.setCluster(cluster);

print 'ADD MANAGED SERVERS TO MACHINE';
server1.setMachine(machine1);
server2.setMachine(machine2);

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

print 'START EDIT MODE';
startEdit();

print 'CREATE FILESTORE FOR SERVER1';
filestore1 = cmo.createFileStore('FileStore1');
filestore1.setDirectory(beahome + pathseparator + 'configuration' + pathseparator + 'applications' + pathseparator + domainname);
targets = filestore1.getTargets();
targets.append(server1);
filestore1.setTargets(targets);

print 'CREATE JMS SERVER FOR SERVER1';
jmsserver1 = cmo.createJMSServer('JMSServer1');
jmsserver1.setPersistentStore(filestore1);
jmsserver1.setTargets(targets);

targets.remove(server1);
targets.append(server2);

print 'CREATE FILESTORE FOR SERVER2';
filestore2 = cmo.createFileStore('FileStore2');
filestore2.setDirectory(beahome + pathseparator + 'configuration' + pathseparator + 'applications' + pathseparator + domainname);
filestore2.setTargets(targets);

print 'CREATE JMS SERVER FOR SERVER2';
jmsserver2 = cmo.createJMSServer('JMSServer2');
jmsserver2.setPersistentStore(filestore2);
jmsserver2.setTargets(targets);

targets.remove(server2);
targets.append(cluster);

print 'CREATE JMS SYSTEM MODULE';
module = cmo.createJMSSystemResource('SystemModule');
module.setTargets(targets);

print 'CREATE SUBDEPLOYMENT';
module.createSubDeployment('SubDeployment');
cd('/JMSSystemResources/SystemModule/SubDeployments/SubDeployment');
set('Targets',jarray.array([ObjectName('com.bea:Name=JMSServer1,Type=JMSServer'), ObjectName('com.bea:Name=JMSServer2,Type=JMSServer')], ObjectName));
cd('/');

resource = module.getJMSResource();

print 'CREATE CONNECTION FACTORY';
resource.createConnectionFactory('ConnectionFactory');
connectionfactory = resource.lookupConnectionFactory('ConnectionFactory');
connectionfactory.setJNDIName('jms/ConnectionFactory');
connectionfactory.setDefaultTargetingEnabled(true);
connectionfactory.getTransactionParams().setTransactionTimeout(3600);
connectionfactory.getTransactionParams().setXAConnectionFactoryEnabled(true);

print 'CREATE UNIFORM DISTRIBUTED QUEUE';
resource.createUniformDistributedQueue('DistributedQueue');
distributedqueue = resource.lookupUniformDistributedQueue('DistributedQueue');
distributedqueue.setJNDIName('jms/CompanyQueue');
distributedqueue.setLoadBalancingPolicy('Round-Robin');
distributedqueue.setSubDeploymentName('SubDeployment');

print 'SAVE AND ACTIVATE CHANGES';
save();
activate(block='true');

print 'SHUTDOWN THE ADMIN SERVER';
shutdown(block='true');

Application

The Spring configuration file looks as follows

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
                <prop key="java.naming.provider.url">t3://192.168.1.50:9001,192.168.1.50:9002</prop>
            </props>
        </property>
    </bean>
    <bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="jms/ConnectionFactory"/>
    </bean>
    <bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate"/>
        <property name="jndiName" value="jms/CompanyQueue"/>
    </bean>
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="defaultDestination" ref="destination"/>
    </bean>
    <bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
        <property name="workManagerName" value="java:comp/env/default"/>
        <property name="resourceRef" value="true"/>
    </bean>
    <bean id="timerListener" class="org.springframework.scheduling.commonj.ScheduledTimerListener">
        <property name="delay" value="10000"/>
        <property name="period" value="30000"/>
        <property name="runnable" ref="sender"/>
    </bean>
    <bean id="timerFactory" class="org.springframework.scheduling.commonj.TimerManagerFactoryBean">
        <property name="timerManagerName" value="java:comp/env/tm/default"/>
        <property name="resourceRef" value="true"/>
        <property name="scheduledTimerListeners">
            <list>
                <ref bean="timerListener"/>
            </list>
        </property>
    </bean>
    <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="destination"/>
        <property name="messageListener" ref="receiver"/>
        <property name="taskExecutor" ref="taskExecutor"/>
    </bean>
    <bean id="sender" class="model.logic.JMSSender">
        <property name="jmsTemplate" ref="jmsTemplate"/>
    </bean>
    <bean id="receiver" class="model.logic.JMSReceiver"/>
</beans>

Here, we use the WebLogic TimerManager to send messages every 30 seconds to the distributed queue. The messages are picked up by the SimpleMessageListenerContainer, for which the threads are managed by the default WebLogic WorkManager. The JMSSender looks as follows

package model.logic;

import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Random;

public class JMSSender implements Runnable {

    private Random generator = new Random();
    private JmsTemplate jmsTemplate;

    public JMSSender() {
    }

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage() {
        getJmsTemplate().send(new MessageCreator(){
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage();
                message.setText(Long.toString(Math.abs(generator.nextLong()), 36));
                return message;
            }
        });
    }

    public void run() {
        sendMessage();
    }
}

and JMSReceiver looks as follows

package model.logic;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class JMSReceiver implements MessageListener {
    public JMSReceiver() {
    }

    public void onMessage(Message message) {
        TextMessage text = (TextMessage)message;
        try {
            message.acknowledge();
            System.out.println("received the following message: " + text.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

Deploy the application. Open the logging of the specific servers to which the application was deployed to check how the messages are balanced between the servers by the distributed queue:

Apr 11, 2012 11:23:15 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Apr 11, 2012 11:23:15 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.XmlWebApplicationContext@1ea13672: display name [Root WebApplicationContext]; startup date [Wed Apr 11 11:23:15 CEST 2012]; root of context hierarchy
Apr 11, 2012 11:23:15 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Apr 11, 2012 11:23:15 AM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.XmlWebApplicationContext@1ea13672]: org.springframework.beans.factory.support.DefaultListableBeanFactory@1eac97e6
Apr 11, 2012 11:23:15 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1eac97e6: defining beans [jndiTemplate,connectionFactory,destination,jmsTemplate,taskExecutor,timerListener,timerFactory,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Apr 11, 2012 11:23:16 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 503 ms
received the following message: yqd0zvqllt58
received the following message: vnsw07fqhsmt
received the following message: 1secm492wxbhd
received the following message: 132hz54mtxvae
received the following message: 1nerzk2kcq41t
received the following message: orj9cmwyvvm4
received the following message: z7kz7vecuvoj
received the following message: 1elg1cszki0gz
received the following message: f283omdowrmm
received the following message: 4tfqfrek6tlm
received the following message: 1lka0mjnvjway
received the following message: n6uuuepdjqw1
received the following message: jz3rgzfp5myt
received the following message: iwvbwuvkotb2
received the following message: 1px4ymopgu0z4
received the following message: 1dvaf4do8nnb1
received the following message: vij9m5bilbtc
received the following message: 2vetjvvw9t1l
Apr 11, 2012 11:23:15 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Apr 11, 2012 11:23:15 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.web.context.support.XmlWebApplicationContext@1e6a873c: display name [Root WebApplicationContext]; startup date [Wed Apr 11 11:23:15 CEST 2012]; root of context hierarchy
Apr 11, 2012 11:23:15 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Apr 11, 2012 11:23:15 AM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.web.context.support.XmlWebApplicationContext@1e6a873c]: org.springframework.beans.factory.support.DefaultListableBeanFactory@1e761a31
Apr 11, 2012 11:23:15 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1e761a31: defining beans [jndiTemplate,connectionFactory,destination,jmsTemplate,taskExecutor,timerListener,timerFactory,org.springframework.jms.listener.SimpleMessageListenerContainer#0,sender,receiver]; root of factory hierarchy
Apr 11, 2012 11:23:15 AM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 520 ms
received the following message: zvbtb316a5w0
received the following message: 1ur1serspbtls
received the following message: 1bcvfhi0f0vs9
received the following message: pfjts61vsryf
received the following message: 1nbp2sq86n1xm
received the following message: 1e6d9szm0sdx7
received the following message: 1juogzbawdyj9
received the following message: 148bbdq1t4mam
received the following message: 1mhaz3f8zzl1t
received the following message: 13hh354hcrf78
received the following message: 8y4n5g11ah5r
received the following message: c9zu7pcorr0r
received the following message: 4q3rgdshqjo9
received the following message: k3y8jygax551
received the following message: 108s63prt2rjl
received the following message: 1qgseev65213l
received the following message: 1utzdoiyw2ynt

By using a distributed queue the message processing load is balanced between the servers to which queue is targetted.

References

[1] WebLogic Server Documentation.


JMS Message Delivered To Busy Instance of JMS Consumer

Shashidhar Mruthyunjaya

Shashidhar Mruthyunjaya

DISCLAIMER:

In this article we may see an abnormal behaviors of weblogic. Which may not be necessarily a BUG but it is always to be aware of such behavior while using Weblogic. The idea behind making this page is just to make awareness among the WebLogic Admins to be alert specially when the some of these behaviors are related to WebLogic Security/JMS.

Some of the behaviors of WebLogic which may be due to inappropriate Security/JMS implementation in the Security/JMS system of WebLogic, Even if in some cases it is work as designed, Still it suggests to keep an eye on it and try to make those features more enhanced. Some of them are now fixed by the Application Server Vendor but still some need to be fixed or enhanced. The intentions here are not to point to the weak points of any Application Server but solely to make people aware about such strange or uncommon behaviors.

==========================================================================

We specially thank to “Mr. Shashidhar Mruthyunjaya” (MiddlewareMagic Subscriber) for making us aware about an abnormal behavior of WebLogic JMS subsystem as part of his comment in http://middlewaremagic.com/weblogic/?page_id=6231#comment-5504

The below code is shared by Shashidhar, we have just  made few changes in sending the messages just to make it more understanding for others and we have tested this code as well as were able to replicated this issue in WLS 10.3.0.

Again thanking Mr. Shashidhar for taking time for us and sharing the complete details about the issue and the steps to reproduce it. Shashidhar also proactively reported this issue to Oracle WebLogic Support team and it resulted in

Bug 13436706 – JMS MESSAGE DELIVERED TO BUSY INSTANCE OF CONSUMER

The Abnormal behavior is happening on weblogic 10.3.2, but I think this bug is lurking in all weblogic servers starting from weblogic 6.x onwards I think. Send 17 messages quickly in succession, 1st one as Long running and other 16 short running messages. You will notice that all the 16 messages run, but the 17th message gets stuck behind the 1st message which is a long running one (and is still running). This is because we have 16 sessions by default and due to weblogic bug, it just cycles around these 16 sessions without checking for free sessions (which there are plenty as short running messages complete quickly and free their JMS sessions)

Basic buggy behavior with weblogic jms code is that it is not looking for free JMS session (or consumer), but going around in a cycle allocating new JMS message arrived to the next session – NOT the next free session as expected. So if the session is already running a long-running message(transaction), the new message will get stuck behind this and will wait until the long-running message finishes. This was causing us frustration as we have some transaction running for hours together!!

==========================
Instructions to simulate
==========================
JMS Configuration: Create JMS step-up by using the WLST script given in the article Setup JMS with Unit-Of-Order using WLST , Script , however you can put “unit.of.order.value = 0″ in the domain.properties file.

Step1) You need to create an EJB project and include ResourceAdapterMDB.java in the same, deploy this

Step2) Create a new Java Project and include this TestMsgSender.java file within that project src.

Step3) Once all setup is done and checking that the EJB is active and listening to the Queue, run the TestMsgSender which has a main method,
which will send 17 messages, 1st one Long running (10 min) and other 16 short running messages. You will notice that the 17th message gets stuck behind the 1st message which is a long running one.

NOTE: Change the queue name and connection factory name and other values to match yours in both these files if required.

ResourceAdapterMDB.java:

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import weblogic.javaee.TransactionTimeoutSeconds;

/**
* Message driven bean for the Resource Adapter This bean is configured to
* listen to the ResAdptQueue queue
*
* @author Shashidhar Mruthyunjaya
*
*/
@MessageDriven(activationConfig = {
	@ActivationConfigProperty(propertyName = "messageListenerInterface", propertyValue = "javax.jms.MessageListener"),
	@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
	@ActivationConfigProperty(propertyName = "MaxDeliveryCnt", propertyValue = "1"),
	@ActivationConfigProperty(propertyName = "ConnectionFactoryName", propertyValue="My_CF")
}
, mappedName="My_Q")
/*
* @TransactionTimeoutSeconds – Resource Adapter Transaction is set to 24 hours
* 24 x 60 x60 = 86400 seconds.
*/
@TransactionTimeoutSeconds(86400)
public class ResourceAdapterMDB implements MessageListener {

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {

TextMessage textMessage = (TextMessage) message;

long millisToSleep = 1000 * 2 ;//2 sec

try {
if("msg= LONG_RUNNING".equals(textMessage.getText())) {
 millisToSleep = 1000 * 60 * 1 ; // Sleep for 1 min
}
} catch (JMSException e) {
e.printStackTrace();
}

try {
Thread.sleep(millisToSleep);
System.out.println("\t==> MyMDB Received: "+ textMessage.getText() +", time= "+ millisToSleep/1000 +" sec.");
}
catch (InterruptedException e) {
e.printStackTrace();
}
catch (JMSException e) {
            e.printStackTrace();
        }
}
}

Step4) Following is the code for the JMS Message Sender “TestMsgSender.java” :

import java.util.Date;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.NamingException;
import weblogic.jndi.Environment;

/**
* Provides the capability to send asynchronous messages to the defined target
* queues
*
* @author Shashidhar Mruthyunjaya
*
*/
public class TestMsgSender {
private static final String QUEUE = "My_Q";
private static final String CONN_FACTORY = "My_CF";
private static final String WEBLOGIC_USER = "weblogic";
private static final String WEBLOGIC_PASSWORD = "weblogic";
private static final String WEBLOGIC_URL = "t3://localhost:7001";

public void sendMsg(String textMessage) {
QueueConnection connection = null;
QueueSender sender = null;
QueueSession session = null;

try {
QueueConnectionFactory qcf = (QueueConnectionFactory) jndiLookup(CONN_FACTORY);
Queue targetQ = (Queue) jndiLookup(QUEUE);
connection = qcf.createQueueConnection();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
sender = session.createSender(targetQ);
TextMessage textMsg = session.createTextMessage();
textMsg.setText(textMessage);
sender.send(textMsg);
System.out.println(textMsg+": Sent at "+(new Date()));
} catch (Exception e) {
System.out.println("Exception occurred while sending message: " + e.getMessage());
throw new RuntimeException(e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
System.out.println("Exception occurred while closing jms connection " + e.getMessage());
throw new RuntimeException(e);
} finally {
connection = null;
}
}
}
}

/**
* Helper method to perform JNDI lookup
*
* @param name
* @return
* @throws NamingException
*/
private Object jndiLookup(String name) throws NamingException {
Environment env = new Environment();
env.setProviderUrl(WEBLOGIC_URL);
env.setSecurityPrincipal(WEBLOGIC_USER);
env.setSecurityCredentials(WEBLOGIC_PASSWORD);
Context ctx = env.getInitialContext();

// Context ctx = new InitialContext();
Object ret = ctx.lookup(name);
ctx.close();

return ret;
}

public static void main(String[] args) throws InterruptedException {
TestMsgSender genericMsgSender = new TestMsgSender();

String textMessage;

// Send a long running message here
textMessage = "msg= LONG_RUNNING";
genericMsgSender.sendMsg(textMessage);

// Send 16 short running message here
for (int i = 1; i < 17; i++) {
genericMsgSender.sendMsg("msg= "+i);
Thread.sleep(1000 * 1); // wait for 1 sec
}
System.out.println("++++++++++++++++++ DONE Sending all messages ++++++++++++++++++");
}
}

Out put of the above code can be seen in the below snap-short which would make things much more easier to understand

Now from the above snap-shot you can see that the first messages “msg= LONG_RUNNING” was sent which takes 60 seconds to get consumed and just after that 16 more messages are been sent which should get consumed within 2 seconds, that makes total of 17 messages are been sent. You would notice that the messages from msg=1 to msg=15 has been consumed by the MDB within 2 seconds for each, but as the first message is still not been consumed due to which the last messages msg=16 (i.e. 17th number message) is been stuck behind and only gets consumed when the first message get consumed.

Workaround/Solution for this Issue:

================================================
Updated on : 16-Dec-2011 Based on Comment: http://middlewaremagic.com/weblogic/?p=7653#comment-5662

We need to define connection-factory-jndi-name” in “weblogic-ejb-jar.xml” along with setting MaximumMessages to 1 . This somehow makes this issue go away. After these changes the messages get allocated to a free consumer!!
.

.
.
Thanks


Setup JMS with Unit-Of-Order using WLST

Ravish Mody

Yesterday one of our subscribers Miguel had asked us using comment how to configure the Unit-of-Order property using WLST script, as he was already able to create a JMSServer, JMSModule and JMS Connection Factory using a wlst script but was having an issue setting the  Unit-of-Order property, hence it seemed to be a great topic to create an article on it. With below single script you can create and target following components.

  1. JMSServer
  2. JMSModule
  3. Queue
  4. ConnectionFactory
  5. Unit-Of-Order

As usual we have used a properties file which has all the required details in it like the JMSServer, JMSModule, Queue, ConnectionFactory names, targets name etc. This way you would not have to modify anything in your actual WLST script.

Setup JMS with Unit-Of-Order using WLST

Step1). Create a Directory somewhere in your file system like : “C:\WLST\JMS

Step2). Write a Properties filedomain.properties inside “C:\WLST\JMS” like following:

# 1 - Connecting details
server.url = t3://localhost:7001
username = weblogic
password = weblogic

# 2 - JMSServer details
jms.server.name = My_JMSServer
store.name = MyJDBCStore
tragated.jms.server.name = AdminServer

# 3 - SystemModule Details
system.module.name = My_SystemModule
tragated.system.module.name = AdminServer

# 4 - ConnectionFactory Details
connection.factory.name = My_ConnectionFactory
connection.factory.jndi.name = My_CF

# 5 - Unit Of Order Details
unit.of.order.value = 1

# 6 - SubDeployment & Queue Details
queue.sub.deployment.name = Sub_My_Queue
queue.name = My_Queue
queue.jndi.name = My_Q

Step2). Now in the same directory write the following WLST Script “JMS_Setup.py” like following:

#############################################################################
#
# @author Copyright (c) 2010 - 2011 by Middleware Magic, All Rights Reserved.
#
#############################################################################

from java.io import FileInputStream
import java.lang
import os
import string

propInputStream = FileInputStream("domain.properties")
configProps = Properties()
configProps.load(propInputStream)

# 1 - Connecting details
serverUrl = configProps.get("server.url")
Username = configProps.get("username")
Password = configProps.get("password")

# 2 - JMSServer details
jmsServerName = configProps.get("jms.server.name")
storeName = configProps.get("store.name")
tragatedJMSServerName = configProps.get("tragated.jms.server.name")

# 3 - SystemModule Details
systemModuleName = configProps.get("system.module.name")
tragatedSystemModuleName = configProps.get("tragated.system.module.name")

# 4 - ConnectionFactory Details
connectionFactoryName = configProps.get("connection.factory.name")
ConnectionFactoryJNDIName = configProps.get("connection.factory.jndi.name")

# 5 - Unit Of Order Details
unitOfOrderValue = configProps.get("unit.of.order.value")

# 6 - SubDeployment & Queue Details
queueSubDeploymentName = configProps.get("queue.sub.deployment.name")
queueName = configProps.get("queue.name")
queueJNDIName = configProps.get("queue.jndi.name")

redirect('wlst.log','false')

# 1 - Connecting to the Destination
connect(Username,Password,serverUrl)

edit()

# 2 - JMSServer details
print "================== JMSSever ==================="
startEdit()
cmo.createJMSServer(jmsServerName)
print "Created a JMSServer !!"
cd('/Deployments/'+jmsServerName)
cmo.setPersistentStore(getMBean('/JDBCStores/'+storeName))
print "PersistentStore has been set for the JMSServer !!"
set('Targets',jarray.array([ObjectName('com.bea:Name='+tragatedJMSServerName+',Type=Server')], ObjectName))
print "Targeted the JMSServer !!"
activate()
print ""

# 3 - SystemModule Details
print "================== SystemModule ==================="
startEdit()
cd('/')
cmo.createJMSSystemResource(systemModuleName)
print "Created a SystemModule !!"
cd('/SystemResources/'+systemModuleName)
set('Targets',jarray.array([ObjectName('com.bea:Name='+tragatedSystemModuleName+',Type=Server')], ObjectName))
print "Targeted the SystemModule !!"
activate()
print ""

# 4 - ConnectionFactory Details
print "================== ConnectionFactory ==================="
startEdit()
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName)
cmo.createConnectionFactory(connectionFactoryName)
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName+'/ConnectionFactories/'+connectionFactoryName)
cmo.setJNDIName(ConnectionFactoryJNDIName)
print "Created a ConnectionFactory !!"
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName+'/ConnectionFactories/'+connectionFactoryName+'/SecurityParams/'+connectionFactoryName)
cmo.setAttachJMSXUserId(false)
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName+'/ConnectionFactories/'+connectionFactoryName)
cmo.setDefaultTargetingEnabled(true)
print "Targeted the ConnectionFactory !!"
activate()
print ""

# 5 - Unit Of Order Details
print "================== Unit Of Order ==================="
startEdit()
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName+'/ConnectionFactories/'+connectionFactoryName+'/DefaultDeliveryParams/'+connectionFactoryName)
cmo.setDefaultUnitOfOrder(unitOfOrderValue)
print "Changed Unit Of Order !!"
activate()
print ""

# 6 - SubDeployment & Queue Details
print "================== SubDeployment & Queue ==================="
startEdit()
cd('/SystemResources/'+systemModuleName)
cmo.createSubDeployment(queueSubDeploymentName)
print "Created a SubDeployment for Queue !!"
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName)
cmo.createQueue(queueName)
print "Created a Queue !!"
cd('/JMSSystemResources/'+systemModuleName+'/JMSResource/'+systemModuleName+'/Queues/'+queueName)
cmo.setJNDIName(queueJNDIName)
cmo.setSubDeploymentName(queueSubDeploymentName)
cd('/SystemResources/'+systemModuleName+'/SubDeployments/'+queueSubDeploymentName)
set('Targets',jarray.array([ObjectName('com.bea:Name='+jmsServerName+',Type=JMSServer')], ObjectName))
print "Targeted the Queue to the created subdeployment !!"
activate()
print ""

cmd = "rm -f wlst.log"
os.system(cmd)

Step3). Now Open a Command/Shell Prompt and then run the “setWLSEnv.sh” script to set the CLASSPATH and PATH environment variables. Run the “. ./setWLSEnv.sh” by adding two DOTs separated by a single space …..before the actual script like following : (use ‘cd’ command to move inside the <BEA_HOME>/wlserver_10.3/server/bin) then run the following command….
. ./setWLSEnv.sh

Note: Here The first DOT represents that set the Environment in the current Shell, AND the second ./ represents execute the script from the current directory.

Step4). Run the Above WLST Script like following:

java weblogic.WLST JMS_Setup.py

Following would be the Output

java weblogic.WLST JMS_Setup.py

Initializing WebLogic Scripting Tool (WLST) ...

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands

================== JMSSever ===================
Created a JMSServer !!
PersistentStore has been set for the JMSServer !!
Targeted the JMSServer !!

================== SystemModule ===================
Created a SystemModule !!
Targeted the SystemModule !!

================== ConnectionFactory ===================
Created a ConnectionFactory !!
Targeted the ConnectionFactory !!

================== Unit Of Order ===================
Changed Unit Of Order !!

================== SubDeployment & Queue ===================
Created a SubDeployment for Queue !!
Created a Queue !!
Targeted the Queue to the created subdeployment !!

Regards,

Ravish Mody


Fun with Spring

In this post we are going to have a little fun with Spring. We show how dependency injection works, how to provide aspects to beans, see how to integrate Hibernate, how to obtain resources such as data sources, JMS connection factories and JMS queues from JNDI, how to create message-driven pojos, how to use commonj WorkManagers on which the thread model of WebLogic is based such that Spring does not spawn unmanaged threads, and if that was not enough see how to use Spring MVC. How good can it get, I love this stuff, hope you do too!

What is Spring?

Spring is a container/framework for dependence injection and aspect oriented programming (AOP), i.e.,

  • Dependency injection – objects get their dependencies in a passive manner, instead of creating objects by hand.
  • Aspect oriented – cohesive development by separating application logic from system services, such as transactions.
  • Container – contains and manages the lifecycle and configuration of the application objects.
  • Framework – configure and compose complex applications by using simple components.

Dependency injection

In a Spring-based application, the objects live in the Spring container. The container creates the objects, wires the objects, configures the objects and manages the objects lifecycle. Two Spring containers are available:

  • Bean factories (BeanFactory provide the basis for dependency injection.
  • Application contexts (ApplicationContext built upon bean factories by providing application services.

The following gives an example of how bean wiring (creating assocations between application components) works:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
                <value>model/entities/Department.hbm.xml</value>
                <value>model/entities/Employee.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            </props>
        </property>
    </bean>
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="jdbc/exampleDS"/>
        <property name="resourceRef" value="true"/>
    </bean>
</beans>

Note that in the example above properties are referenced by using ${hibernate.dialect}, in order for this to work we need to add the following bean:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<list>
			<value>classpath:spring.properties</value>
		</list>
	</property>
</bean>

in which spring.properties is a custom file that is placed in the root of the classpath. This information is provided to Spring by using classpath:. Note that the properties file is nothing more than a basic key/value pair file, i.e.,

hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
hibernate.current_session_context_class=thread
hibernate.show_sql=false
hibernate.format_sql=false

As we are talking about properties. When configuring we sometimes run into the issue that we need to configure a bean that contains a java.util.Date. By default, we can configure bean properties by using basic Java objects, such as Strings. To be able to set properties of type java.util.Date as a String we have to configure the following:

<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="java.util.Date" value-ref="customDateEditor"/>
		</map>
	</property>
</bean>
<bean id="customDateEditor" class="org.springframework.beans.propertyeditors.CustomDateEditor">
	<constructor-arg ref="simpleDateFormat"/>
	<constructor-arg value="false"/>
</bean>
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
	<constructor-arg value="dd-MM-yyyy"/>
</bean>

Spring now automatically converts a String to a Date object. Note that this is not handled by Spring itself but by a JavaBeans API feature. The interface PropertyEditor provides the possibility to map String values to other types. A handy implementation of the PropertyEditor interface is PropertyEditorSupport that contains two useful methods:

  • getAsText – gets the property value as a String.
  • setAsText(String value) – sets the property value by parsing a given String.

When we try to map a String value to another type, the setAsText method is called to execute the conversion. Spring provides a couple of editors which are based on PropertyEditorSupport, such as CustomDateEditor(DateFormat dateFormat, boolean allowEmpty). This class is used to convert a String to a Date and vice versa. The CustomEditorConfigurer is a so-called BeanFactoryPostProcessor which load editors in the BeanFactory by calling the registerCustomEditor method.

Aspect oriented programming

Systems typically consist of a number of components that are each responsible for a specific piece of functionality. These components usually use some kind of system service, such as transactions. System services are used by multiple components. Now we could duplicate code into each of the components that use system services or we could configure it and Spring handle it. Of course we are going for the latter option as we do not want our code to be obfuscated by code that is not part of the core functionality.

It may help to think of aspects as blankets that cover many components of an application. At its core, an application consists of modules that implement business functionality. With AOP, we can cover the core application with layers of functionality. These layers can be applied declarative throughout the application in a manner without the core application even knowing they exist. This is a powerful concept, as it keeps the transaction (and, for example, security) concerns from littering the application’s core business logic.

Aspects help to modularize cross-cutting concerns. In short, a cross-cutting concern can be described as any functionality that affects multiple points of an application. Security, for example, is a cross-cutting concern in that many methods in an application can have security rules applied to them. A common object-oriented technique for reusing common functionality is to apply inheritance or delegation. But inheritance can lead to a brittle object hierarchy if the same base class is used throughout an application, and delegation can be cumbersome because complicated calls to the delegate object may be required. Aspects offer an alternative to inheritance and delegation that can be cleaner in many circumstances. With AOP, we still define the common functionality in one place, but we can declarative define how and where this functionality is applied without having to modify the class to which we are applying the new feature. Cross-cutting concerns can now be modularized into special objects called aspects. This has two benefits:

  • First, the logic for each concern is now in one place, as opposed to being scattered all over the code base.
  • Second, our service modules are now cleaner since they only contain code for their primary concern (or core functionality) and secondary concerns have been moved to aspects.

Aspects are often described in terms of advice, pointcuts, and joinpoints. Aspects have a purpose – a job that they are meant to do. In AOP terms, the job of an aspect is called advice. Advice defines both the what and the when of an aspect. In addition to describing the job that an aspect will perform, advice addresses the question of when to perform the job. Should it be applied before a method is invoked? After the method is invoked? Both before and after method invocation? Or should it only be applied if a method throws an exception?

An application has opportunities for an advice to be applied. These opportunities are known as joinpoints. A joinpoint is a point in the execution of the application where an aspect can be plugged in. This point could be a method being called, an exception being thrown, or a field being modified. These are the points where the aspect’s code can be inserted into the normal flow of the application to add new behavior.

An aspect does not necessarily advise all joinpoints in an application. Pointcuts help narrow down the joinpoints advised by an aspect. If an advice defines the what and when of aspects then pointcuts define the where. A pointcut definition matches one or more joinpoints at which advice should be woven. Often we specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns.

An aspect is the merger of advice and pointcuts. Taken together, advice and pointcuts define everything there is to know about an aspect – what it does and where and when it does it. Spring employs AOP to provide enterprise services such as declarative transactions. Transactions allow us to group several operations into a single unit of work that either fully happens or fully does not happen. When writing to a database, we must ensure that the integrity of the data is maintained by performing the updates within a transaction.

A transaction can be described by the acronym ACID. In short, ACID stands for:

  • Atomic – Transactions are made up of one or more activities bundled together as a single unit of work. Atomicity ensures that all the operations in the transaction happen or that none of them happen. If all the activities succeed, the transaction is a success. If any of the activities fail, the entire transaction fails and is rolled back.
  • Consistent – Once a transaction ends, the system is left in a state that is consistent with the business that it models. The data should not be corrupted with respect to reality.
  • Isolated – Transactions should allow multiple users to work with the same data, without each user’s work getting tangled up with the others. Therefore, transactions should be isolated from each other, preventing concurrent reads and writes to the same data from occurring. (Note that isolation typically involves locking rows and/or tables in a database.)
  • Durable – Once the transaction has completed, the results of the transaction should be made permanent so that they will survive any sort of system crash. This typically involves storing the results in a database or some other form of persistent storage.

Spring provides support for both programmatic and declarative transaction management support. While programmatic transaction management affords flexibility in precisely defining transaction boundaries in the code, declarative transactions help to decouple an operation from its transaction rules. Choosing between programmatic and declarative transaction management is largely a decision of fine-grained control versus convenience. Spring does not directly manage transactions. Instead, it comes with a selection of transaction managers that delegate responsibility for transaction management to a platform-specific transaction implementation provided by either JTA or the persistence mechanism. To use a transaction manager, we must declare it in the application context, for example,

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"/>
</bean>

Spring’s support for declarative transaction management is implemented through Spring’s AOP framework. A Spring transaction is sort of an aspect that ‘wraps’ a method with transactional boundaries. In Spring, declarative transactions are defined with transaction attributes. A transaction attribute is a description of how transaction policies should be applied to a method. To declare transactions we must set five parameters, which govern how transaction policies are administered:

  • Propagation behavior – Defines the boundaries of the transaction with respect to the client and to the method being called.
  • Isolation level – Defines how much a transaction may be impacted by the activities of other concurrent transactions. Realizing that perfect isolation can impact performance and because not all applications will require perfect isolation, sometimes it is desirable to be flexible with regard to transaction isolation.
  • Read-only – If a transaction performs only read operations against the underlying data store, the data store may be able to apply certain optimizations that take advantage of the read-only nature of the transaction. By declaring a transaction as read-only, we give the underlying data store the opportunity to apply those optimizations as it sees fit.
  • Transaction timeout – For an application to perform well, its transactions can not carry on for a long time. Suppose that the transaction becomes unexpectedly long-running. Because transactions may involve locks on the underlying data store, long-running transactions can tie up database resources unnecessarily. Instead of waiting it out, we can declare a transaction to automatically roll back after a certain number of seconds.
  • Rollback rules – A set of rules that define what exceptions prompt a rollback and which ones do not. By default, transactions are rolled back only on runtime exceptions and not on checked exceptions. However, we can declare that a transaction be rolled back on specific checked exceptions as well as runtime exceptions. Likewise, we can declare that a transaction not roll back on specified exceptions, even if those exceptions are runtime exceptions.

Declarative transaction management is accomplished by proxying classes with Spring’s TransactionProxyFactoryBean, for example,

<bean id="departmentDAOTarget" class="model.logic.DepartmentDAOBean">
	<property name="hibernateTemplate" ref="hibernateTemplate"/>
</bean>
<bean id="departmentDAO" parent="transactionTemplate">
	<property name="target" ref="departmentDAOTarget"/>
	<property name="proxyInterfaces" value="model.logic.DepartmentDAO"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
	<property name="transactionManager" ref="transactionManager"/>
	<property name="transactionAttributes">
		<props>
			<prop key="add*">PROPAGATION_REQUIRED</prop>
			<prop key="remove*">PROPAGATION_REQUIRED</prop>
			<prop key="update*">PROPAGATION_REQUIRED</prop>
			<prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
		</props>
	</property>
</bean>

The second bean entry (transactionTemplate) is an abstract declaration which is used as a template. From this abstract declaration, we can make any number of beans transactional by using transactionTemplate as the parent declaration of the bean.

The DepartmentDAOBean has no idea that its methods are being called within the context of a transaction. If any object makes calls directly to the DepartmentDAOBean, those calls will not be transactional. Instead, collaborating objects should invoke methods on the proxy that is produced by TransactionProxyFactoryBean. The proxy will ensure that transactional rules are applied and then proxy the call to the real DepartmentDAOBean. Therefore, rather than inject the service directly into those objects that use it, we will inject the DepartmentDAOBean proxy into those objects. This means that the proxy produced by TransactionProxyFactoryBean must pretend to be a DepartmentDAOBean. That is the purpose of the proxyInterfaces property. Here we are telling TransactionProxyFactoryBean to produce a proxy that implements the DepartmentDAO interface. The transactionManager property supplies the appropriate transaction manager bean.

TransactionProxyFactoryBean will use the transaction manager to start, suspend, commit, and roll back transactions based on the transaction attributes defined in the transactionAttributes property of TransactionProxyFactoryBean. The transactionAttributes property declares which methods are to be run within a transaction and what the transaction attributes are to be. This property is given a props collection where the key of each prop is a method name pattern and the value defines the transaction attributes for the method(s) selected. The value of each prop given to the transactionAttributes property is a comma-separated value (Propagation Behavior, Isolation Level – Optional, Is the transaction read only? – Optional, Rollback Rules (-Exception, +Exception) – Optional). Note that a ‘-’ prefix forces a rollback while a ‘+’ prefix specifies commit (this allows commit on unchecked exceptions). In the case of the DepartmentDAOBean, we are declaring that all methods whose name starts with add should be run within a transaction. Methods starting with find support transactions (but do not necessarily require a transaction) and are read-only.

We can also accomplish this by using annotations, the only thing we need to add to the Spring application context is:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
	...
	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
	<tx:annotation-driven/>
</beans>

Note that the transaction-manager attribute of tx:annotation-driven defaults to transactionManager and needs to be specified when an other name is used. The tx:annotation-driven configuration element tells Spring to examine the beans in the application context and to look for beans that are annotated with Transactional. For every bean that is Transactional, tx:annotation-driven will automatically advise it with transaction advice. The transaction attributes of the advice will be defined by parameters of the Transactional annotation. In our code we can use the following:

package model.logic;

import org.hibernate.criterion.DetachedCriteria;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public abstract class GenericHibernateSpringDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {

    private Class<T> persistentClass;
    private HibernateTemplate hibernateTemplate;

    public GenericHibernateSpringDAO() {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            setPersistentClass((Class<T>) parameterizedType.getActualTypeArguments()[0]);
        } else {
            System.out.println("Not an instance of parameterized type: " + type);
        }
    }

    public Class<T> getPersistentClass() {
        return persistentClass;
    }

    public void setPersistentClass(Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    public T addEntity(T entity) {
        getHibernateTemplate().save(entity);
        return entity;
    }

    public void removeEntity(ID id) {
        getHibernateTemplate().delete(findEntity(id));
    }

    public void updateEntity(T entity) {
        getHibernateTemplate().update(entity);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public T findEntity(ID id) {
        return (T) getHibernateTemplate().get(getPersistentClass(), id);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<T> findEntities() {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(getPersistentClass());
        return getHibernateTemplate().findByCriteria(detachedCriteria);
    }
}

Integrating Hibernate

Hibernate (an object/relational mapping framework) defines a technique to map an object oriented data model to a relational data model.

Even though there are many steps to the data access process, we are only actively involved in a couple of those steps. The carrier itself is responsible for driving the process. We are only involved when we need to be; the rest is just ‘taken care of’. This mirrors a powerful design pattern: the Template Method pattern. A template method defines the skeleton of a process. The process itself is fixed; it never changes. At certain points, however, the process delegates its work to a subclass to fill in some implementation-specific details. In software terms, a template method delegates the implementation-specific portions of the process to an interface. Different implementations of this interface define specific implementations of this portion of the process.

This is the same pattern that Spring applies to data access. No matter what technology we are using, certain data access steps are required. For example, we always need to obtain a connection to our data store and clean up resources when we are done. These are the fixed steps in a data access process. But each data access method we write is slightly different. We query for different objects and update the data in different ways. These are the variable steps in the data access process. Spring separates the fixed and variable parts of the data access process into two distinct classes, templates and callbacks:

  • Templates manage the fixed part of the process.
  • Callbacks handle the variable part, such as custom data access code.

Spring’s template classes handle the fixed parts of data access – controlling transactions, managing resources, and handling exceptions. Meanwhile, the specifics of data access as they pertain to the application – creating statements, binding parameters, and marshaling result sets – are handled in the callback implementation. In practice, this makes for an elegant framework because all we have to worry about is the data access logic. Spring comes with several templates to choose from, depending on the persistence platform choice. When using Hibernate then we can use HibernateTemplate as was done in the example above.

Applications that have a persistent state must in one way or another have interaction with the persistence provider, when a certain in-memory state must be persisted to the database (or vice versa). In the case of Hibernate this is the interface Session. Each Session is associated with a persistence context. A persistence context is some sort of cache that tracks object changes in a certain transaction. Note that Spring’s HibernateTemplate provides an abstract layer for the Session interface.

The complete Spring configuration looks as follows:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
    <bean id="departmentDAO" class="model.logic.DepartmentDAOBean">
        <property name="hibernateTemplate" ref="hibernateTemplate"/>
    </bean>
    <bean id="employeeDAO" class="model.logic.EmployeeDAOBean">
        <property name="hibernateTemplate" ref="hibernateTemplate"/>
    </bean>
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
                <value>model/entities/Department.hbm.xml</value>
                <value>model/entities/Employee.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            </props>
        </property>
    </bean>
    <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
        <property name="driverType" value="${jdbc.driverClassName}"/>
        <property name="URL" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:spring.properties</value>
            </list>
        </property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    <tx:annotation-driven/>
</beans>

Obtaining beans

Now, that we have the configuration in place we need a way to obtain the configured beans. In Spring, objects are not responsible for finding or creating other objects that they need to do their job. Instead, they are given references to the objects that they collaborate with by the container. The act of creating these associations between application objects is the essence of dependency injection and is commonly referred to as wiring. Spring comes with several container implementations that can be categorized into two distinct types:

  • Bean factories provide basic dependency injection support. A bean factory is an implementation of the Factory design pattern, i.e., it is a class whose responsibility it is to create and dispense beans and is able to create associations between collaborating objects as they are instantiated. A bean factory also takes part in the lifecycle of a bean, such as making calls to initialization and destruction methods, if those methods are defined.
  • Application contexts provide application framework services, such as the ability to resolve textual messages from a properties file and the ability to publish application events to interested event listeners.

Before we can retrieve a bean from the application context, it needs to be created. This can be accomplished by using ClassPathXmlApplicationContext (Loads a context definition from an XML file located in the classpath, treating context definition files as classpath resources). Once we have an ApplicationContext instance we can use the getBean method to retrieve a bean, for example,

package model.utils;

import model.logic.DepartmentDAO;
import model.logic.EmployeeDAO;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Utilities {

    private static ApplicationContext context;

    private Utilities() {
    }

    static {
        context = new ClassPathXmlApplicationContext("spring-config.xml");
    }

    public static DepartmentDAO getDepartmentDAO() {
        return (DepartmentDAO) context.getBean("departmentDAO");
    }

    public static EmployeeDAO getEmployeeDAO() {
        return (EmployeeDAO) context.getBean("employeeDAO");
    }
}

A difference between an application context and a bean factory is how singleton beans are loaded. A bean factory lazily loads all beans, deferring bean creation until the getBean method is called. An application context is a bit smarter and preloads all singleton beans upon context start up. To take advantage of the opportunities offered by Spring to customize bean creation we have to understand the lifecycle:

  1. Instantiate – instantiate the bean.
  2. Populate properties – inject the bean’s properties.
  3. Set bean name – if the bean implements BeanNameAware, the bean’s ID is passed to setBeanName.
  4. Set bean factory – if the bean implements BeanFactoryAware, the bean factory is passed to setBeanFactory. If the bean implements ApplicationContextAware interface, the setApplicationContext method is called.
  5. Postprocess – if there are any BeanPostProcessors, Spring calls the postProcessBeforeInitialization method.
  6. Initialize beans – if the bean implements InitializingBean, the afterPropertiesSet method will be called. If the bean has an init-method declared, the specified method will be called.
  7. Postprocess – if there are any BeanPostProcessors, the postProcessAfterInitialization method will be called.
  8. At this point the bean is ready to be used by the application and will remain in the bean factory until it is no longer needed.
  9. Destroy bean – if the bean implements DisposableBean, the destroy method will be called. If the bean has a destroy-method declared, the specified method will be called.

By default, all Spring beans are singletons, i.e., when the container dispenses a bean it will always hand out the exact same instance of the bean. When declaring a bean in Spring, we have the option of declaring a scope for the bean. For example, to let Spring produce a new bean instance each time one is needed, we must declare the bean’s scope attribute to be prototype, for example

<bean id="cacheStore" class="com.tangosol.coherence.hibernate.HibernateCacheStore" scope="prototype">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

Spring offers a handful scoping options:

  • singleton – scopes the bean definition to a single instance per container (default).
  • prototype – allows a bean to be instantiated any number of times (once per use).
  • request – scopes a bean definition to an HTTP request. Only valid when used with a web capable Spring context (such as with Spring MVC).
  • session – scopes a bean definition to an HTTP session. Only valid when used with a web capable Spring context (such as with Spring MVC).
  • global-session – scopes a bean definition to a global HTTP session. Only valid when used in a portlet context.

Usually, the scope will be singleton, sometimes the prototype scope is useful, for example, when other programs need to manage the life cycle.

Obtain objects from JNDI

In distributed applications some components need to access resources, such as, database servers and messaging systems. Resources are typically identified by a unique name – a JNDI name and are in general configured on an application server. When we need objects that are not configured as beans in Spring but in JNDI we can use JndiObjectFactoryBean.

When the application is deployed on the server where the resources, such as data sources, are configured we can proceed as follows. To create a data source bean we add the following entry:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiName" value="jdbc/exampleDS"/>
	<property name="resourceRef" value="true"/>
</bean>

In order for this work we need to add the following entry in the web.xml file

<resource-ref>
	<res-ref-name>jdbc/exampleDS</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

The resource-ref makes the resource known to the (Web) container. The container makes the resources available in the local java:comp/env/ environment.

To access resources remotely, we first have to define a JNDI template, for example,

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
	<property name="environment">
		<props>
			<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
			<prop key="java.naming.provider.url">t3://hostname:portnumber</prop>
		</props>
	</property>
</bean>

To create beans of remote resources, we can use the following

<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiTemplate" ref="jndiTemplate"/>
	<property name="jndiName" value="jms/ConnectionFactory"/>
</bean>
<bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiTemplate" ref="jndiTemplate"/>
	<property name="jndiName" value="jms/Queue"/>
</bean>

Now that we know how to retrieve resources, let us use this in an example that uses messaging.

Messaging

JMS can be divided into two functional areas, i.e., production and consumption of messages. The building blocks of a JMS application constist of:

  • Administrative objects (usually configured in an application server)
    • Connection factories – @Resource(name = "jms/ConnectionFactory", type = ConnectionFactory.class) ConnectionFactory connectionFactory;
    • Destinations (Queues or Topics) – @Resource(name = "jms/CompanyQueue", type = Queue.class) Queue destination;
  • Connections – Connection connection = connectionFactory.createConnection();
  • Sessions – Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
  • Message producers – MessageProducer messageProducer = session.createProducer(destination);
  • Message consumers – MessageConsumer messageConsumer = session.createConsumer(otherDestination);
  • Messages – ObjectMessage message = session.createObjectMessage(); message.setObject(person); messageProducer.send(message);

Just as that Spring offers templates for data access it also provides a JMSTemplate. The JMSTemplate can be used for message production and synchronous message consumption. For asynchronous consumption (such as message-driven beans), a message listener container can be used. To create a JMSTemplate we can use the following:

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
	<property name="connectionFactory" ref="connectionFactory"/>
	<property name="defaultDestination" ref="destination"/>
</bean>

Code that uses a JMSTemplate only needs to implement a callback interface. For example, MessageCreator creates a message using a Session that is provided by the JMSTemplate:

package model.logic;

import model.entities.Department;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.*;

public class JMSSender {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendTextMessage() {
        getJmsTemplate().send(new MessageCreator(){
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage();
                message.setText("Testing the JMS set-up one-two, one-two test");
                return message;
            }
        });
    }

    public void sendMessage(final Department department) {
        getJmsTemplate().send(new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                ObjectMessage message = session.createObjectMessage();
                message.setObject(department);
                return message;
            }
        });
    }
}

To inject the JMSTemplate into the JMSSender we can use the following:

<bean id="sender" class="model.logic.JMSSender">
	<property name="jmsTemplate" ref="jmsTemplate"/>
</bean>

To receive messages asynchronously we can use message listener containers (Spring’s equivalent to message-driven beans). A message listener container is used to receive messages from a queue and drive the MessageListener that is injected into it. The listener container is responsible for all the threading of message consumption and dispatches to the listener for processing. Three message listeners are available:

The following shows an example message listener container configuration:

<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory"/>
	<property name="destination" ref="destination"/>
	<property name="messageListener" ref="receiver"/>
</bean>
<bean id="receiver" class="model.logic.JMSReceiver"/>

in which the JMSReceiver looks as follows:

package model.logic;

import model.entities.Department;
import javax.jms.*;

public class JMSReceiver implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            TextMessage text = (TextMessage) message;
            try {
                message.acknowledge();
                System.out.println("received the following message: " + text.getText());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }

        if (message instanceof ObjectMessage) {
            ObjectMessage objectMessage = (ObjectMessage) message;
            try {
                message.acknowledge();
                Department department = (Department) objectMessage.getObject();
                System.out.println("received the following message: " + department);
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

When we choose the run Spring JMS on an application server, for example WebLogic, we run into the fact that the threads spawned by Spring are unmanaged. To make sure the thread are managed we can couple the message listener container to a work manager. First we add the following entry to the web.xml file

<resource-ref>
	<res-ref-name>default</res-ref-name>
	<res-type>commonj.work.WorkManager</res-type>
	<res-auth>Container</res-auth>
	<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

Now, we are able to create the following TaskExecutor:

<bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
	<property name="workManagerName" value="java:comp/env/default"/>
	<property name="resourceRef" value="true"/>
</bean>

To inject the TaskExecutor into the message listener container we can use:

<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory"/>
	<property name="destination" ref="destination"/>
	<property name="messageListener" ref="receiver"/>
	<property name="taskExecutor" ref="taskExecutor"/>
</bean>

Spring MVC

Spring MVC is designed around a DispatcherServlet that dispatches requests to handlers. The default handler is a Controller interface. In general, application controllers will subclass controllers such as AbstractController. Basically, the following classes will be involved in processing a request:

  • The DispatcherServlet reads the configuration file (<dispatcher-servlet-name>-servlet.xml and creates a respository with configuration objects.
  • The DispatcherServlet acts as a front controller and delegates requests to other components (Controllers).
  • Based on handler mappings it is determined which controller should process the request. The handler mapping uses the URL to choose the appropriate controller.
  • The Controller processes the request that results in information that should be returned to the requester:
    • The information is made available as a Model.
    • The information is handed to a View (for example, a JSP), such that the Model can be formatted as HTML.
  • The controller packages the Model data and the name of the View into a ModelAndView object and gives this to the DispatcherServlet.
  • The DispatherServlet uses a ViewResolver to determine the View.
  • The DispatherServlet passes the Model data to the View.

Let us just give an example of how to set this up. First, we have to configure the DispatcherServlet in the web.xml file, for example,

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-config.xml</param-value>
   </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/spring/*</url-pattern>
    </servlet-mapping>
</web-app>

Note that based on the servlet-name a certain Spring configuration file is loaded. In this case the name of the configuration file must be spring-servlet.xml. By using the context parameter contextConfigLocation we can control which other configuration files must be loaded from the classpath.

To create a Controller, we subclass the AbstractController. In this case we need to override the handleRequestInternal method, for example,

package userinterface.controllers;

import model.entities.Department;
import model.logic.DepartmentDAO;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

public class DepartmentController extends AbstractController {

    private DepartmentDAO departmentDAO;

    public DepartmentController() {
    }

    public DepartmentDAO getDepartmentDAO() {
        return departmentDAO;
    }

    public void setDepartmentDAO(DepartmentDAO departmentDAO) {
        this.departmentDAO = departmentDAO;
    }

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        List<Department> departments = getDepartmentDAO().findEntities();
        if (departments != null) {
            for (Department department: departments) {
                System.out.println(department);
            }
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("departments", departments);
        modelAndView.setViewName("departments");
        return modelAndView;
    }
}

To inject the DepartmentDAO instance into the Controller we add the following in the spring-servlet.xml file:

<bean name="departmentController" class="userinterface.controllers.DepartmentController">
	<property name="departmentDAO" ref="departmentDAO"/>
</bean>

Note that the departmentDAO bean is configured in the spring-config.xml file.

The HandlerMapping is set-up as follows. By using a handler mapping we map incoming requests to appropriate handlers. When the request comes in, the DispatcherServlet will hand it over to the handler mapping to let it inspect the request and come up with the appropriate HandlerExecutionChain. Then the DispatcherServlet will execute the handler. A very handy handler mapping is the SimpleUrlHandlerMapping, for example,

<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
		<props>
			<prop key="/departments.html">departmentController</prop>
		</props>
	</property>
</bean>

This handler mapping routes requests for departments.html to the departmentController.

Next, we need to resolve the view. To this end, Spring provides view resolvers, which enable us to render models in a browser without tying them to a specific view technology. The ViewResolver interface provides a mapping between view names and actual views. When using JSP as the view technology we can use the InternalResourceViewResolver, for example,

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/"/>
	<property name="suffix" value=".jsp"/>
</bean>

The following shows an example of a view in this case a JSP page (departments.jsp):

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
	<head><title>Departments</title></head>
	<body>
		<h1>Hello</h1>
		<table>
			<c:forEach items="${departments}" var="department">
				<tr>
					<td><c:out value="${department.departmentName}"/></td>
				</tr>
			</c:forEach>
		</table>
	</body>
</html>

Note that the departments variable is set by the departmentController through the returned ModelAndView instance. When we put a request in the form of http://hostname:port/<context-root>/spring/departments.html the following happens:

  • As we configured an url-mapping in the web.xml file, the /spring/* part of the URL makes sure that the DispatcherServlet handles the request.
  • The DispatcherServlet receives a request with the following URL /departments.html.
  • By using the SimpleUrlHandlerMapping the DispatcherServlet determines which controller to use. In this case it finds a mapping to the DepartmentController.
  • The DispatcherServlet passes the request to the DepartmentController.
  • The DepartmentController creates a ModelAndView object, in which
    • The Model contains a List with all the Department objects and maps this to a property named departments.
    • The View contains a logical name, in this case departments.
  • The DispatcherServlet uses the InternalResourceViewResolver to determine which JSP the Model has to render.
  • The DispatcherServlet passes the request to the departments.jsp page.

References

[1] Walls, “Spring in Action”, Manning, 2008. All you ever wanted to know about Spring (also a good reference when you are going for your Spring certification).
[2] The Spring Framework – Reference Documentation.


[Updated Version] Steps to Move Messages From One Queue To Another Queue Using WLST

Ravish Mody

Today one of our subscribers Pavan had  come back saying that the WLST script which was created by us previously How to Move Messages From One Queue To Another Queue Using WLST was only “working to move the messages from one queue to another queue which are on the same servers only“  by using comment , and as per his requirement they need to move the messages from Queue-1 to Queue-2 which are targeted to Server-1 and Server-2 respectively. Hence I have just made few modification in my previous script and thus this is the update version of it

I had to add two URL’s to get connected to the with the respective servers and also added two System Module this way you can move the messages from any Source Server to the Destination Server which can be in the same module or if the destination server is in different system module too this way its more flexible. All this can be done using the same script just give the domain and there servers details in the domain.properties files and you are good to go.

[Updated Version] Steps to Move Messages From One Queue To Another Queue Using WLST

Step1). Create a Directory somewhere in your file system like : “C:\WLST\Move_Messages”

Step2). Write a Properties filedomain.properties inside “C:\WLST\Move_Messages” like following:

######################
#    Destination
######################
new.server.url=t3://localhost:8003
new.system.module.name=SystemModule2
new.tragated.server.name=MS-2
new.jms.server.name=JMSServer2
new.queue.name=Q2

######################
#	Source
######################
old.server.url=t3://localhost:8002
old.system.module.name=SystemModule1
old.tragated.server.name=MS-1
old.jms.server.name=JMSServer1
old.queue.name=Q1

username=weblogic
password=weblogic

Step2). Now in the same directory write the following WLST Script “move_messages.py” like following:

#############################################################################
#
# @author Copyright (c) 2010 - 2011 by Middleware Magic, All Rights Reserved.
#
#############################################################################
from java.io import FileInputStream
import java.lang
import os
import string

propInputStream = FileInputStream("domain.properties")
configProps = Properties()
configProps.load(propInputStream)

newserverUrl = configProps.get("new.server.url")
newsystemModuleName = configProps.get("new.system.module.name")
newTragatedServerName = configProps.get("new.tragated.server.name")
newJmsServerName = configProps.get("new.jms.server.name")
newQueueName = configProps.get("new.queue.name")

oldserverUrl = configProps.get("old.server.url")
oldsystemModuleName = configProps.get("old.system.module.name")
oldTragatedServerName = configProps.get("old.tragated.server.name")
oldJmsServerName = configProps.get("old.jms.server.name")
oldQueueName = configProps.get("old.queue.name")

Username = configProps.get("username")
Password = configProps.get("password")

# Connecting to the Destination
connect(Username,Password,newserverUrl)
serverRuntime()

print 'Getting the traget...'
cd('/JMSRuntime/'+newTragatedServerName+'.jms/JMSServers/'+newJmsServerName+'/Destinations/'+newsystemModuleName +'!'+ newQueueName)
target = cmo.getDestinationInfo()
print 'Got the traget...'

disconnect()
print ''

# Connecting to the Source
connect(Username,Password,oldserverUrl)
serverRuntime()
cd('/JMSRuntime/'+oldTragatedServerName+'.jms/JMSServers/'+oldJmsServerName+'/Destinations/'+oldsystemModuleName +'!'+ oldQueueName)

print 'Moving the messages to the new traget...'
cmo.moveMessages('',target)
print 'Messages have been moved to the traget Successfully !!!'

print '===================================================================================='
print 'Messages from queue: "'+oldQueueName+'" have been moved to the new queue: "'+newQueueName+'" Successfully !!!'
print '===================================================================================='

Step3). Now Open a Command/Shell Prompt and then run the “setWLSEnv.sh” script to set the CLASSPATH and PATH environment variables. Run the “. ./setWLSEnv.sh” by adding two DOTs separated by a single space …..before the actual script like following : (use ‘cd’ command to move inside the <BEA_HOME>/wlserver_10.3/server/bin) then run the following command….
. ./setWLSEnv.sh

Note: Here The first DOT represents that set the Environment in the current Shell, AND the second ./ represents execute the script from the current directory.

Step4). Run the Above WLST Script like following:

java weblogic.WLST move_messages.py

Following would be the Output

java weblogic.WLST move_messages.py

Initializing WebLogic Scripting Tool (WLST) ...

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands

Connecting to t3://localhost:8003 with userid weblogic ...
Successfully connected to managed Server 'MS-2' that belongs to domain 'Domain_8001'.

Warning: An insecure protocol was used to connect to the
server. To ensure on-the-wire security, the SSL port or
Admin port should be used instead.

Location changed to serverRuntime tree. This is a read-only tree with ServerRuntimeMBean as the root.
For more help, use help(serverRuntime)

Getting the traget...
Got the traget...
Disconnected from weblogic server: MS-2

Connecting to t3://localhost:8002 with userid weblogic ...
Successfully connected to managed Server 'MS-1' that belongs to domain 'Domain_8001'.

Warning: An insecure protocol was used to connect to the
server. To ensure on-the-wire security, the SSL port or
Admin port should be used instead.

Moving the messages to the new traget...
Messages have been moved to the traget Successfully !!!
====================================================================================
Messages from queue: "Q1" have been moved to the new queue: "Q2" Successfully !!!
====================================================================================

Note:

Pava you would have to change the line number – 35  & 45 as per your environment

Regards,
Ravish Mody


How to Move Messages From One Queue To Another Queue Using WLST

Ravish Mody

Two days back one of our subscribers Pavan had asked us by using comment , that is it possible to move the messages from one queue to another queue that to using WLST and the answer to that question is YES. It is possible to do the same using WLST and we have created the WLST script.

This is a very simple script which would just collect the target/destination queue where the messages has to be moved to (i.e. new.queue.name) and then would just go to the queue where the messages has to be moved from(i.e. old.queue.name). All this can be done using the same script just give the domain and there servers details in the domain.properties files and you are good to go.

Steps to Move Messages From One Queue To Another Queue Using WLST

Step1). Create a Directory somewhere in your file system like : “C:\WLST\Move_Messages”

Step2). Write a Properties filedomain.properties inside “C:\WLST\Move_Messages” like following:

server.url=t3://localhost:8001
username=weblogic
password=weblogic

system.module.name=MySystemModule

old.server.name=AdminServer
old.jms.server.name=Admin_JMSServer
old.queue.name=Admin_Queue

new.tragated.server.name=AdminServer
new.jms.server.name=MyJMSServer
new.queue.name=MyTestQueue

Step2). Now in the same directory write the following WLST Script “move_messages.py” like following:

#############################################################################
#
# @author Copyright (c) 2010 - 2011 by Middleware Magic, All Rights Reserved.
#
#############################################################################
from java.io import FileInputStream
import java.lang
import os
import string

propInputStream = FileInputStream("domain.properties")
configProps = Properties()
configProps.load(propInputStream)

serverUrl = configProps.get("server.url")
Username = configProps.get("username")
Password = configProps.get("password")

systemModuleName = configProps.get("system.module.name")

oldServerName = configProps.get("old.server.name")
oldJmsServerName = configProps.get("old.jms.server.name")
oldQueueName = configProps.get("old.queue.name")

newTragatedServerName = configProps.get("new.tragated.server.name")
newJmsServerName = configProps.get("new.jms.server.name")
newQueueName = configProps.get("new.queue.name")

connect(Username,Password,serverUrl)

serverRuntime()

print 'Getting the traget...'
cd('/JMSRuntime/'+newTragatedServerName+'.jms/JMSServers/'+newJmsServerName+'/Destinations/'+systemModuleName +'!'+ newQueueName)
target = cmo.getDestinationInfo()
print 'Got the traget...'

cd('/JMSRuntime/'+oldServerName+'.jms/JMSServers/'+oldJmsServerName+'/Destinations/'+systemModuleName +'!'+ oldQueueName)

print 'Moving the messages to the new traget...'
cmo.moveMessages('',target)
print 'Messages have been moved to the traget Successfully !!!'

print '======================================='
print 'Messages from queue: "'+oldQueueName+'" have been moved to the new queue: "'+newQueueName+'" Successfully !!!'
print '======================================='

Step3). Now Open a Command/Shell Prompt and then run the “setWLSEnv.sh” script to set the CLASSPATH and PATH environment variables. Run the “. ./setWLSEnv.sh” by adding two DOTs separated by a single space …..before the actual script like following : (use ‘cd’ command to move inside the <BEA_HOME>/wlserver_10.3/server/bin) then run the following command….
. ./setWLSEnv.sh

Note: Here The first DOT represents that set the Environment in the current Shell, AND the second ./ represents execute the script from the current directory.

Step4). Run the Above WLST Script like following:

java weblogic.WLST move_messages.py

Following would be the Output

java weblogic.WLST move_messages.py

Initializing WebLogic Scripting Tool (WLST) ...

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands

Connecting to t3://localhost:8001 with userid weblogic ...
Successfully connected to Admin Server 'AdminServer' that belongs to domain 'Domain_8001'.

Warning: An insecure protocol was used to connect to the
server. To ensure on-the-wire security, the SSL port or
Admin port should be used instead.

Location changed to serverRuntime tree. This is a read-only tree with ServerRuntimeMBean as the root.
For more help, use help(serverRuntime)

Getting the traget...
Got the traget...
Moving the messages to the new traget...
Messages have been moved to the traget Successfully !!!
=======================================
Messages from queue: "Admin_Queue" have been moved to the new queue: "MyTestQueue" Successfully !!!
=======================================

Note:

Only for Pava you would have to do the following changes
- Instead for line number – 34
cd('/JMSRuntime/'+oldServerName+'.jms/JMSServers/'+oldJmsServerName+'/Destinations/'+systemModuleName +'!'+newJmsServerName+'@'+ newQueueName)
- Instead for line number - 38
cd('/JMSRuntime/'+oldServerName+'.jms/JMSServers/'+oldJmsServerName+'/Destinations/'+systemModuleName +'!'+oldJmsServerName+'@'+ oldQueueName)

Regards, Ravish Mody


How To Pause and Resume A Queue Using WLST

Ravish Mody

Today one of our subscribers Pavan had asked us by using comment , Here based on the query of Pavan we are writing the following article, which would Pause the Queue at Runtime even when the AdminServer/AdminConsole  is unavailable, thus we took out sometime and just now created a WLST script which would do the same job very easily.

In this script you can choose from which end do you want Queue to get paused, say if you want to pause the messages to be inserted then you can use pauseProduction , if you need that the consumption from the queue should stop then use pauseConsumtion and if you want that Queue should not insert or consume then use both. This way you can pause the queue as per your requirement, also with this you can resume the queue too by using resumeProduction, resumeConsumption or both of them. All this can be done using the same script just give the domain and there servers details in the domain.properties files and you are good to go.

Steps to Pause and Resume A Queue Using WLST

Step1). Create a Directory somewhere in your file system like : “C:\WLST\Pause_Queue”

Step2). Write a Properties filedomain.properties inside “C:\WLST\Pause_Queue” like following:

server.url=t3://10.10.10.10:7003
username=weblogic
password=weblogic
tragated.server.name=MS-1
jms.server.name=JMSServer-1
system.module.name=SystemModule-1
queue.name=Q-1

Step2). Now in the same directory write the following WLST Script “pause_queue.py” like following:

#############################################################################
#
# @author Copyright (c) 2010 - 2011 by Middleware Magic, All Rights Reserved.
#
#############################################################################
from java.io import FileInputStream
import java.lang
import os
import string

propInputStream = FileInputStream("domain.properties")
configProps = Properties()
configProps.load(propInputStream)

serverUrl = configProps.get("server.url")
Username = configProps.get("username")
Password = configProps.get("password")

tragatedServerName = configProps.get("tragated.server.name")
jmsServerName = configProps.get("jms.server.name")
systemModuleName = configProps.get("system.module.name")
queueName = configProps.get("queue.name")

connect(Username,Password,serverUrl)
serverRuntime()
cd('JMSRuntime/'+tragatedServerName+'.jms/JMSServers/'+jmsServerName+'/Destinations/'+systemModuleName +'!'+ queueName)

##################
# To PAUSE A Queue
##################

######## This would NOT let the messages Insert in the queue but it would let the messages get Consumed  ###########
cmo.pauseProduction()
print 'Queue: "', queueName ,'" has been PRODUCTION Paused Successfully'

######## This would let the messages Insert in the queue but it would NOT let the messages get Consumed  ###########
cmo.pauseConsumption()
print 'Queue: "', queueName ,'" has been CONSUMPTION Paused Successfully'

###################
# To RESUME A Queue
###################

#cmo.resumeProduction()
#print 'Queue: "', queueName ,'" has been PRODUCTION Resumed Successfully'

#cmo.resumeConsumption()
#print 'Queue: "', queueName ,'" has been CONSUMPTION Resumed Successfully'

Step3). Now Open a Command/Shell Prompt and then run the “setWLSEnv.sh” script to set the CLASSPATH and PATH environment variables. Run the “. ./setWLSEnv.sh” by adding two DOTs separated by a single space …..before the actual script like following : (use ‘cd’ command to move inside the <BEA_HOME>/wlserver_10.3/server/bin) then run the following command….
. ./setWLSEnv.sh

Note: Here The first DOT represents that set the Environment in the current Shell, AND the second ./ represents execute the script from the current directory.

Step4). Run the Above WLST Script like following:

java weblogic.WLST pause_queue.py

Following would be the Output

java weblogic.WLST pause_queue.py

Initializing WebLogic Scripting Tool (WLST) ...

Welcome to WebLogic Server Administration Scripting Shell

Type help() for help on available commands

Connecting to t3://10.10.10.10:7003 with userid weblogic ...
Successfully connected to managed Server 'MS-1' that belongs to domain 'Domain_7001'.

Warning: An insecure protocol was used to connect to the
server. To ensure on-the-wire security, the SSL port or
Admin port should be used instead.

Location changed to serverRuntime tree. This is a read-only tree with ServerRuntimeMBean as the root.
For more help, use help(serverRuntime)

Queue: " Q-1 " has been PRODUCTION Paused Successfully
Queue: " Q-1 " has been CONSUMPTION Paused Successfully

Regards,
Ravish Mody


  • Testimonials

  • RSS Middleware Magic – JBoss

  • Receive FREE Updates


    FREE Email updates of our new posts Enter your email address:



  • Magic Archives

  • Sitemeter Status

  • ClusterMap 7-Nov-2011 till Date

  • ClusterMap 6-Nov-2010 till 7-Nov-2011

  • Copyright © 2010-2012 Middleware Magic. All rights reserved. | Disclaimer