JMS with Weblogic Server and EJB MDB

Here is a complete and short example of asynchronous development using JMS and a Message-Driven Bean (MDB).
First the procedure to configure inside Weblogic Server a JMS server in order to create a queue, then the code for the producer/sender of a message to the queue and then the code for a consumer (MDB).

1) Weblogic configuration
a) Create the JMS server

JMS_Weblogic1

JMS_Weblogic2

JMS_Weblogic3

JMS_Weblogic4

b) Create a JMS module
JMS_Weblogic5

JMS_Weblogic6

JMS_Weblogic7

JMS_Weblogic8

JMS_Weblogic9

JMS_Weblogic10

JMS_Weblogic11

JMS_Weblogic12

c) Create a destination (queue or topic)

JMS_Weblogic13

JMS_Weblogic14

JMS_Weblogic15

JMS_Weblogic16

d) Create a connection factory

JMS_Weblogic17

JMS_Weblogic18

JMS_Weblogic19

2) Create a producer

For the producer, I got strongly inspired by the code found in this blog. This is a clean example of a producer/sender so no need to reinvent the wheel here.

import java.util.Hashtable;

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.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JMSProducer {

	private static InitialContext initialContext = null;
	private static QueueConnectionFactory queueConnectionFactory = null;
	private static QueueConnection queueConnection = null;
	private static QueueSession queueSession = null;
	private static Queue queue = null;
	private static QueueSender queueSender = null;
	private static TextMessage textMessage = null;
	private static final String CONNECTIONFACTORY_NAME = "ConnectionFactory-Test";
	private static final String QUEUE_NAME = "jms/QueueTest";

	public JMSProducer() {
		super();
	}

	public static void sendMessageToDestination(String messageText) {
		// create InitialContext
		Hashtable properties = new Hashtable();
		properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
		properties.put(Context.PROVIDER_URL, "t3://localhost:7001");
		properties.put(Context.SECURITY_PRINCIPAL, "weblogic");
		properties.put(Context.SECURITY_CREDENTIALS, "weblogic1");
		try {
			initialContext = new InitialContext(properties);
		} catch (NamingException ne) {
			ne.printStackTrace(System.err);
		}
		System.out.println("InitialContext : " + initialContext.toString());
		// create QueueConnectionFactory
		try {
			queueConnectionFactory = (QueueConnectionFactory) initialContext.lookup(CONNECTIONFACTORY_NAME);
		} catch (NamingException ne) {
			ne.printStackTrace(System.err);
		}

		System.out.println("QueueConnectionFactory : " + queueConnectionFactory.toString());
		// create QueueConnection
		try {
			queueConnection = queueConnectionFactory.createQueueConnection();
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("QueueConnection : " + queueConnection.toString());
		// create QueueSession
		try {
			queueSession = queueConnection.createQueueSession(false, 0);
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("QueueSession : " + queueSession.toString());
		// lookup Queue
		try {
			queue = (Queue) initialContext.lookup(QUEUE_NAME);
		} catch (NamingException ne) {
			ne.printStackTrace(System.err);
		}

		System.out.println("Queue : " + queue.toString());
		// create QueueSender
		try {
			queueSender = queueSession.createSender(queue);
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("QueueSender : " + queueSender.toString());
		// create TextMessage
		try {
			textMessage = queueSession.createTextMessage();
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("TextMessage : " + textMessage.toString());
		try {
			textMessage.setText(messageText);
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("TextMessage : " + textMessage.toString());
		// send message
		try {
			queueSender.send(textMessage);
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("Message sent.");
		// clean up
		try {
			textMessage = null;
			queueSender.close();
			queueSender = null;
			queue = null;
			queueSession.close();
			queueSession = null;
			queueConnection.close();
			queueConnection = null;
			queueConnectionFactory = null;
			initialContext = null;
		} catch (JMSException jmse) {
			jmse.printStackTrace(System.err);
		}

		System.out.println("Cleaned up done.");
	}

	public static void main(String args[]) {
		sendMessageToDestination("test");
	}
}

3) Create a consumer

This MDB consumer just prints out something in the log when it consumes the message in the queue.
The lifecycle methods are empty but they could be used to clean up resources for instance.

import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJBException;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@MessageDriven(mappedName = "jms/QueueTest", activationConfig = {
		@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
		@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") })
public class TestJMSSABean implements MessageDrivenBean, MessageListener {

	/** The Constant LOGGER. */
	protected static final Logger LOGGER = LoggerFactory.getLogger(TestJMSSABean.class);

	public TestJMSSABean() {
	}

	@Override
	public void onMessage(Message message) {
		try {
			if (message instanceof TextMessage) {
				System.out.println(((TextMessage) message).getText());
				LOGGER.trace("***************** : " + ((TextMessage) message).getText());
			} else {
				System.out.println(message.getJMSMessageID());
				LOGGER.trace("***************** : " + message.getJMSMessageID());
			}
		} catch (JMSException ex) {
			LOGGER.trace("***************** ERROR : " + ex.getMessage());
		}
	}

	@Override
	public void ejbRemove() throws EJBException {
		// TODO Auto-generated method stub
	}

	@Override
	public void setMessageDrivenContext(MessageDrivenContext arg0) throws EJBException {
		// TODO Auto-generated method stub
	}
}

Prevent Weblogic Server from wrapping data types

By default, Weblogic Server wraps data types such as Struct, Blob, Array, Clob, etc.

For instance, oracle.sql.STRUCT becomes weblogic.jdbc.wrapper.Struct_oracle_sql_STRUCT.

That can become a problem if for instance you’re expecting a data type :


oracle.sql.STRUCT objSTRUCT = (oracle.sql.STRUCT) callableStatement.getObject(2);

This would raise the following error :


java.lang.ClassCastException: weblogic.jdbc.wrapper.Struct_oracle_sql_STRUCT

To avoid it, one can simply disable that wrapping in the Weblogic administration console :

Go to Services > Data sources > Select your datasource > “Connection pools” tab > Advanced  :

uncheck “Wrap data types”

WrapDataTypes_WeblogicWrapDataTypes2

Note : Weblogic Server version used is 10.3.5

[JPA 2] Adding support to WebLogic 10.3.5 for JPA 2

WebLogic 10.3.5 is JAVA EE 5 certified. However JPA 2 is not part of the JAVA EE 5 (2006) specifications, it is part of the JAVA EE 6 (2009) specifications.
So since WebLogic Server implements the Java EE 5 specification, it is not required to support JPA 2. However it is possible to add support to WebLogic 10.3.5 for JPA 2. Look no further, the answer is of course on Oracle WebLogic website: Using Oracle TopLink with Oracle WebLogic Server

A default WebLogic installation already contains the files needed :

javax.persistence_1.0.0.0_2-0-0.jar
com.oracle.jpa2support_1.0.0.0_2-0.jar

So if you make the choice to configure things manually then you do not need to install a patch.
It is just a matter of declaring a PRE_CLASSPATH environment variable in the file commEnv.cmd (Windows) or commEnv.sh (Linux) located in the WL_HOME/common/bin/ folder.

Under Windows :

@rem JPA 2 activation

set PRE_CLASSPATH=%BEA_HOME%\modules\javax.persistence_1.0.0.0_2-0-0.jar;%BEA_HOME%\modules\com.oracle.jpa2support_1.0.0.0_2-0.jar

Under Linux :

PRE_CLASSPATH=${BEA_HOME}/modules/javax.persistence_1.0.0.0_2-0-0.jar:${BEA_HOME}/modules/com.oracle.jpa2support_1.0.0.0_2-0.jar
export PRE_CLASSPATH

If you do not configure WebLogic 10.3.5 to support JPA 2 you will get an error message like this one :

nested exception is:
	javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec.
  The exception is:java.lang.NoSuchMethodError: javax.persistence.EntityManager.createQuery(Ljava/lang/String;Ljava/lang/Class;)Ljavax/persistence/TypedQuery;;
nested exception is: javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec.
The exception is:java.lang.NoSuchMethodError: javax.persistence.EntityManager.createQuery(Ljava/lang/String;Ljava/lang/Class;)Ljavax/persistence/TypedQuery;

javax.ejb.EJBException: what do i do: seems an odd quirk of the EJB spec.
 The exception is:java.lang.NoSuchMethodError: javax.persistence.EntityManager.createQuery(Ljava/lang/String;Ljava/lang/Class;)Ljavax/persistence/TypedQuery;

There is no need to edit the file weblogic-application.xml and to add some additional prefered application package. That one for instance works :

<?xml version="1.0" encoding="UTF-8" ?>
<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:wls="http://www.bea.com/ns/weblogic/90"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd http://www.bea.com/ns/weblogic/90 http://www.bea.com/ns/weblogic/90/weblogic-application.xsd">
	<prefer-application-packages>
		<package-name>org.apache.commons.lang.*</package-name>
		<package-name>org.eclipse.persistence.*</package-name>
	</prefer-application-packages>
	<application-param>
		<param-name>webapp.encoding.default</param-name>
		<param-value>UTF-8</param-value>
	</application-param>
	<session-descriptor>
		<persistent-store-type>replicated_if_clustered</persistent-store-type>
	</session-descriptor>
</weblogic-application>