JMS with Glassfish v3 & ActiveMQ 5.7

2012/12/21

I was about to compare Message Driven Beans (MDB) with Spring JMS and started to setup a small application with an MDB and a servlet. Messages should be sent from the servlet to a ActiveMQ queue and the MDB should then receive them. The application was to be deployed on Glassfish v3 (3.1.2.2) I ran into a few problems which I will document for you to find in this blog post.

First I created my application using the built-in JMS broker shipped with Glassfish v3 (OpenMQ). This was quite straight forward and worked nicely.

Then I downloaded ActiveMq 5.7.0 and started a broker from the installation dir:

  1. bin/activemq start
  2. Tail log: tail -f data/activemq.log
  3. Load admin console: http://localhost:8161/admin/

To switch over to ActiveMQ as a JMS broker, I followed the steps described in this post by Geert Shuring. Versions are updated to current of today:

  1. Copy the following jars from ActiveMQ lib and lib/optional folders to the Glassfish lib folder: slf4j-api-1.6.6.jar, slf4j-log4j12-1.6.6.jar and log4j-1.2.17.jar.
  2. Download activemq-rar-5.7.0.rar from here and install as a Resource adapter using Glassfish admin console (http://localhost:4848/). (Application type is automatically detected by Glassfish)
  3. Create a new config, using Glassfish admin console, under Resources -> Resource adapter configs:
    • Resource adapter name = activemq-rar-5.7.0
    • Thread pool id = thread-pool-1
    • Additional properties untouched! (defaultUser / defaultPassword / tcp://localhost:61616 etc)
  4. Create a new Connector connection pool using admin console under Resources -> Connectors -> Connector Connection Pools with:
    • Pool name = amqpool
    • Resource adapter = activemq-rar-5.7.0
    • Connection definition = javax.jms.ConnectionFactory
  5. Create a new Connector Resource using admin console under Resources -> Connectors -> Connector Resources with:
    • JNDI name = amqres
    • Pool name = amqpool
  6. Create a new Admin Object Resource using asadmin tool. Don’t create the resource from the admin console! It caused me problems where the resource could not be found using JNDI. (This is most likely a bug in the admin console.)
    • Start asadmin from Glassfish bin folder
    • Execute command create-admin-object –restype javax.jms.Queue –raname activemq-rar-5.7.0 –property PhysicalName=incomingAmq incomingAmq
    • Check the new resource in the admin console
  7. Now the Glassfish configurations should be ready.

You can check the Connector Connection Pool being connected to ActiveMQ by clicking Ping button on the amqpool object.

After the configurations are ready, it’s time to write some code. These are the important files for the application:

SenderServlet.java:

package dag;

import javax.annotation.Resource;
import javax.jms.*;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SendServlet extends HttpServlet {
@Resource(mappedName = "amqres")
private ConnectionFactory connectionFactory;

@Resource(mappedName = "incomingAmq")
private Queue incoming;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String messageText = request.getParameter("x");
  ServletOutputStream out = response.getOutputStream();
  out.println("<html><body>");
  out.println("<p>ConnectionFactory: "+connectionFactory+"</p>");
  out.println("<p>Queue: "+ incoming + "</p>");
  out.println("<p>Servlet sending '"+messageText+"' to queue.</p>");

  Connection connection;
  try {
    connection = connectionFactory.createConnection();
    Session session = connection.createSession(false,
    Session.AUTO_ACKNOWLEDGE);
    MessageProducer messageProducer = session.createProducer(incoming);
    TextMessage message = session.createTextMessage();
message.setText(messageText);
    messageProducer.send(message);
  } catch (JMSException e) {
    throw new RuntimeException(e);
  }
  out.println("Done sneding.");
  out.flush();
}
}

MessageReceiverBean.java

package dag;

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

@MessageDriven(
  activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "incomingAmq")
  }
)
public class MessageReceiverBean implements MessageListener {
  public MessageReceiverBean() {
  }

  @Override
  public void onMessage(Message message) {
    try {
      System.out.println("Received: "+((TextMessage)message).getText());
    } catch (JMSException e) {
      throw new RuntimeException(e);
    }
  }
}

glassfish-ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
 <enterprise-beans>
 <ejb>
 <ejb-name>MessageReceiverBean</ejb-name>
 <mdb-resource-adapter>
 <resource-adapter-mid>activemq-rar-5.7.0</resource-adapter-mid>
 </mdb-resource-adapter>
 </ejb>
 </enterprise-beans>
</glassfish-ejb-jar>

(ejb-jar.xml should not be needed)

Create an ear package and deploy to Glassfish. Go to http://localhost:8080/web/servlet?x=theOatmeal to test the application. What is given in request parameter x should be sent to the JMS queue ‘incomingAmq’ and the MDB should then receive this message from ‘incomingAmq’ and print it on stdout. The physical queue ‘incomingAmq’ will be automatically created by ActiveMQ. Take a look at the AMQ admin console: http://localhost:8161/admin/queues.jsp

Possible problems that may occur:

  • When invoking the SenderServlet you get an error in the log like “javax.naming.NameNotFoundException: incomingAmq not found”, where “incomingAmq” is the resource mappedName in the SernderServlet. This may be caused by creating the Admin Resource Object “incomingAmq” from the admin console. Create it using asadmin as described above! Stacktrace may look like:
javax.naming.NameNotFoundException: incomingAmq not found
	at com.sun.enterprise.naming.impl.TransientContext.doLookup(TransientContext.java:248)
	at com.sun.enterprise.naming.impl.TransientContext.lookup(TransientContext.java:215)
	at com.sun.enterprise.naming.impl.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:77)
	at com.sun.enterprise.naming.impl.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:119)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:505)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:455)
	at javax.naming.InitialContext.lookup(InitialContext.java:392)
        ...
  • If you get the following NullPointerException when invoking the servlet, you may be missing the PhysicalName property on the Admin Resource object ‘incomingAmq’. It should be included in the object if created using the asadmin command above. Otherwise you can add the property using the admin console.
java.lang.NullPointerException
	at org.apache.activemq.command.ActiveMQDestination.hashCode(ActiveMQDestination.java:329)
	at java.util.HashMap.get(HashMap.java:300)
	at org.apache.activemq.openwire.OpenWireFormat.getMarshallCacheIndex(OpenWireFormat.java:517)
	at org.apache.activemq.openwire.v9.BaseDataStreamMarshaller.tightMarshalCachedObject1(BaseDataStreamMarshaller.java:158)
	at org.apache.activemq.openwire.v9.ProducerInfoMarshaller.tightMarshal1(ProducerInfoMarshaller.java:98)
	at org.apache.activemq.openwire.OpenWireFormat.marshal(OpenWireFormat.java:234)
	at org.apache.activemq.transport.tcp.TcpTransport.oneway(TcpTransport.java:183)
        ...
Advertisements

2 Responses to “JMS with Glassfish v3 & ActiveMQ 5.7”

  1. Alejandro said

    This works great, but how do you increase the number of beans, similar to endpointPoolMaxSize

  2. duncan thomson said

    Thanks, this is extremely useful. After a few false starts, and adjusting details for the latest versions of the software, I was able to get this working.

    A couple of comments: In your step 6 you say: “Don’t create the resource from the admin console!” Actually I did use the admin console and it worked fine, so perhaps that was a bug that’s been fixed since you wrote. A more significant comment: The last step, creating a glassfish-ejb-jar.xml file, bothers me. I hate to put server-specific code into my application. (And I consider XML deployment descriptors “code”.) I’d rather configure this in the server, just like all the other server configurations. It seems more logical to that there should be a server config to tell it that this bean should be connected to the Active MQ resource adapter. Is there a way to do that, either using the console, or with asadmin commands?

    Again, thanks for this write-up, it was very useful.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: