Tuesday, March 17, 2015

Sending a message in Java Message Service 2.0 from an Enterprise Java Bean

In this example I will explore the utilization of injection, which works inside containers (in this case the WildFly's Enterprise Java Beans container). I will use the @Resource annotation to initialize the value of a destination, and the annotation @Inject to initialize the JMSContext without requiring any ConnectionFactory whatsoever!

Covering Enterprise JavaBeans (EJBs) is beyond the scope of this particular message, as I covered them in this other message. It is also not my goal to cover the reception of Java Message Service (JMS) messages in an EJB. This I covered in another different message (although for JMS 1.1). All I'm doing here is to send JMS messages from an EJB upon request from a client.

This message is a followup from this one, where I create a queue, before writing a sender and two receivers.

Let us start by writing the Enterprise JavaBean, before going to the client. First, we need an EJB Project. Look for File-->New in your Eclipse menus and pick EJB Project. You may have to switch to Java EE view on the top right corner of the screen to have a view that is similar to the following picture:



Next, we fill the project creation form:


And go for the creation of the EJB, File-->New-->Session Bean (EJB 3.x):

For our example we need the Remote view, as the EJB will be accessed from a different JVM. The no-interface view for accesses from within the same EJB is not necessary, but this option is already selected by default, no big deal about it:

This creates a template class and interface, named EJBJMSSender and EJBJMSSenderRemote, respectively. Let us start with the Remote Interface. We will only define a send() method, accessible by a client:

package ejb;


import javax.ejb.Remote;

@Remote
public interface EJBJMSSenderRemote {
public void send(String message);

}


The most juicy part is the bean implementation itself. Injection makes it so amazingly simple! It is hard to believe that this is actually sending a JMS message, when we think about JMS 1.1. The @Resource annotation will set up the destination (in this case a queue), whereas the @Inject annotation will create a container-managed JMSContext, without any reference to the ConnectionFactory. The container, instead of the application, will take care of creating and deleting the JMSContext.

package ejb;

import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.jms.Destination;
import javax.jms.JMSContext;
import javax.jms.JMSProducer;


/**
 * Session Bean implementation class EJBJMSSender
 */
@Stateless
@LocalBean
public class EJBJMSSender implements EJBJMSSenderRemote {
@Inject
private JMSContext jcontext;

@Resource(lookup = "java:jboss/exported/jms/queue/PlayQueue")
private Destination d;

@Override
public void send(String message) {
JMSProducer jp = jcontext.createProducer();
jp.send(d, message);
}
}


Don't forget to start the server with

./standalone.sh -c standalone-full.xml

from the bin directory and don't forget to create the queue if it doesn't exist yet (again, take a look at this).

We are now in conditions to deploy this EJB. First, we use the contextual menu of the project to deploy it. To do this, we export the project as an EJB JAR file:

And then, we select WildFly's standalone/deployments directory:

The output should look like this, with a list of the EJB JNDI names:


JBAS016002: Processing weld deployment JMSfromEJB.jar
10:08:42,347 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-8) JNDI bindings for session bean named EJBJMSSender in deployment unit deployment "JMSfromEJB.jar" are as follows:

java:global/JMSfromEJB/EJBJMSSender!ejb.EJBJMSSender
java:app/JMSfromEJB/EJBJMSSender!ejb.EJBJMSSender
java:module/EJBJMSSender!ejb.EJBJMSSender
java:global/JMSfromEJB/EJBJMSSender!ejb.EJBJMSSenderRemote
java:app/JMSfromEJB/EJBJMSSender!ejb.EJBJMSSenderRemote
java:module/EJBJMSSender!ejb.EJBJMSSenderRemote
java:jboss/exported/JMSfromEJB/EJBJMSSender!ejb.EJBJMSSenderRemote

10:08:42,411 INFO  [org.jboss.weld.deployer] (MSC service thread 1-8) JBAS016005: Starting Services for CDI deployment: JMSfromEJB.jar
10:08:42,429 INFO  [org.jboss.weld.deployer] (MSC service thread 1-5) JBAS016008: Starting weld service for deployment JMSfromEJB.jar
10:08:42,702 INFO  [org.jboss.as.server] (DeploymentScanner-threads - 1) JBAS018559: Deployed "JMSfromEJB.jar" (runtime-name : "JMSfromEJB.jar")

The server part is ready. Next, we need to invoke the EJB we just wrote. We will create a standard Java Project (File-->New-->Java Project in the standard Java view) and a new class:



The code of the client is simply this:

import javax.naming.InitialContext;
import javax.naming.NamingException;

import ejb.EJBJMSSenderRemote;


public class UseEJB {

public static void main(String[] args) throws NamingException {
EJBJMSSenderRemote bean = InitialContext.doLookup("JMSfromEJB/EJBJMSSender!ejb.EJBJMSSenderRemote");
bean.send("There goes the sun");
}
}

Note that the name in the lookup partially appears in the output of the server. As usual, we need a jndi.properties file to use JNDI. To do this, pick File-->New-->File and write the following:

java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://localhost:8080
jboss.naming.client.ejb.context=true

This properties file needs to be in the same source folder as the class UseEJB:



When we run our Java class, we get a seemingly empty and disappointing result:

Mar 17, 2015 10:44:54 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.2.2.Final
Mar 17, 2015 10:44:54 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.2.2.Final
Mar 17, 2015 10:44:54 AM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version (unknown)
Mar 17, 2015 10:44:54 AM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
Mar 17, 2015 10:44:54 AM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@394e1a0f, receiver=Remoting connection EJB receiver [connection=Remoting connection <56ff6669>,channel=jboss.ejb,nodename=ovelha]} on channel Channel ID befa1070 (outbound) of Remoting connection 7a79f074 to localhost/127.0.0.1:8080
Mar 17, 2015 10:44:54 AM org.jboss.ejb.client.EJBClient <clinit>
INFO: JBoss EJB Client version 2.0.1.Final

In fact, the queue PlayQueue should now have a message. You may run one of the receivers of this message to get the message.

No comments:

Post a Comment