terça-feira, 17 de março de 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.

segunda-feira, 16 de março de 2015

Java Message Service 2.0 with WildFly 8

In a couple of previous messages, I provided examples of Java Message Service (JMS) and JBoss AS 7. In the first, I did the basic, in the following one, I used a number of more advanced features. It's time to move on, and consider the following version of JBoss, which is now WildFly, and, more importantly, the next version of JMS, which is now the 2.0. While WildFly imposed little to no changes in the previous examples, the differences introduce by JMS 2.0 are very worth considering. JMS 2.0 is much less verbose and much easier to use. For details, please refer to this page.

I will write three applications in this example:

  • JMS queue sender
  • JMS synchronous queue receiver
  • JMS asynchronous queue receiver

Configuration

Let us start by launching the server from the bin directory. Note the -c standalone-full.xml option, to enable a configuration where JMS is active:

bin filipius$ ./standalone.sh -c standalone-full.xml
=========================================================================

  JBoss Bootstrap Environment

  JBOSS_HOME: /opt/jboss

  JAVA: java

  JAVA_OPTS:  -server -Xms64m -Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true

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

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
11:44:23,563 INFO  [org.jboss.modules] (main) JBoss Modules version 1.3.3.Final
11:44:24,022 INFO  [org.jboss.msc] (main) JBoss MSC version 1.2.2.Final
11:44:24,118 INFO  [org.jboss.as] (MSC service thread 1-6) JBAS015899: WildFly 8.2.0.Final "Tweek" starting
11:44:27,028 INFO  [org.jboss.as.server] (Controller Boot Thread) JBAS015888: Creating http management service using socket-binding (management-http)
11:44:27,064 INFO  [org.xnio] (MSC service thread 1-8) XNIO version 3.3.0.Final
11:44:27,089 INFO  [org.xnio.nio] (MSC service thread 1-8) XNIO NIO Implementation Version 3.3.0.Final
11:44:27,168 WARN  [org.jboss.as.txn] (ServerService Thread Pool -- 52) JBAS010153: Node identifier property is set to the default value. Please make sure it is unique.
11:44:27,169 INFO  [org.jboss.as.jacorb] (ServerService Thread Pool -- 36) JBAS016300: Activating JacORB Subsystem
11:44:27,169 INFO  [org.jboss.as.security] (ServerService Thread Pool -- 51) JBAS013171: Activating Security Subsystem
11:44:27,182 INFO  [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 35) JBAS010280: Activating Infinispan subsystem.
11:44:27,183 INFO  [org.jboss.as.naming] (ServerService Thread Pool -- 46) JBAS011800: Activating Naming Subsystem
11:44:27,188 INFO  [org.jboss.as.security] (MSC service thread 1-6) JBAS013170: Current PicketBox version=4.0.21.Final
11:44:27,250 INFO  [org.jboss.as.webservices] (ServerService Thread Pool -- 54) JBAS015537: Activating WebServices Extension
11:44:27,292 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017502: Undertow 1.1.0.Final starting
11:44:27,294 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 53) JBAS017502: Undertow 1.1.0.Final starting
11:44:27,304 INFO  [org.jboss.as.jsf] (ServerService Thread Pool -- 42) JBAS012615: Activated the following JSF Implementations: [main]
11:44:27,307 INFO  [org.wildfly.extension.io] (ServerService Thread Pool -- 34) WFLYIO001: Worker 'default' has auto-configured to 8 core threads with 64 task threads based on your 4 available processors
11:44:27,330 INFO  [org.jboss.as.connector.logging] (MSC service thread 1-2) JBAS010408: Starting JCA Subsystem (IronJacamar 1.1.9.Final)
11:44:27,398 INFO  [org.jboss.as.naming] (MSC service thread 1-3) JBAS011802: Starting Naming Service
11:44:27,406 INFO  [org.jboss.as.mail.extension] (MSC service thread 1-7) JBAS015400: Bound mail session [java:jboss/mail/Default]
11:44:27,467 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 30) JBAS010403: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3)
11:44:27,480 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-6) JBAS010417: Started Driver service with driver-name = h2
11:44:27,689 INFO  [org.jboss.remoting] (MSC service thread 1-1) JBoss Remoting version 4.0.6.Final
11:44:27,743 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 53) JBAS017527: Creating file handler for path /opt/jboss/welcome-content
11:44:27,821 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-2) JBAS017525: Started server default-server.
11:44:27,870 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-5) JBAS017531: Host default-host starting
11:44:28,074 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-1) JBAS017519: Undertow HTTP listener default listening on /127.0.0.1:8080
11:44:28,121 WARN  [org.jboss.as.messaging] (MSC service thread 1-8) JBAS011600: AIO wasn't located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO to enable the AIO journal
11:44:28,313 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221000: live server is starting with configuration HornetQ Configuration (clustered=false,backup=false,sharedStore=true,journalDirectory=/opt/jboss/standalone/data/messagingjournal,bindingsDirectory=/opt/jboss/standalone/data/messagingbindings,largeMessagesDirectory=/opt/jboss/standalone/data/messaginglargemessages,pagingDirectory=/opt/jboss/standalone/data/messagingpaging)
11:44:28,318 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221006: Waiting to obtain live lock
11:44:28,391 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221013: Using NIO Journal
11:44:28,419 INFO  [org.jboss.as.jacorb] (MSC service thread 1-6) JBAS016330: CORBA ORB Service started
11:44:28,468 INFO  [org.jboss.as.server.deployment.scanner] (MSC service thread 1-1) JBAS015012: Started FileSystemDeploymentService for directory /opt/jboss/standalone/deployments
11:44:28,540 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-1) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS]
11:44:28,561 INFO  [io.netty.util.internal.PlatformDependent] (ServerService Thread Pool -- 56) Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system unstability.
11:44:28,686 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221043: Adding protocol support CORE
11:44:28,705 INFO  [org.jboss.as.jacorb] (MSC service thread 1-6) JBAS016328: CORBA Naming Service started
11:44:28,782 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221043: Adding protocol support AMQP
11:44:28,792 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221043: Adding protocol support STOMP
11:44:28,854 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221034: Waiting to obtain live lock
11:44:28,854 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221035: Live Server Obtained live lock
11:44:28,980 INFO  [org.jboss.ws.common.management] (MSC service thread 1-3) JBWS022052: Starting JBoss Web Services - Stack CXF Server 4.3.2.Final
11:44:29,127 INFO  [org.jboss.messaging] (MSC service thread 1-1) JBAS011615: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor-throughput acceptor
11:44:29,127 INFO  [org.jboss.messaging] (MSC service thread 1-5) JBAS011615: Registered HTTP upgrade for hornetq-remoting protocol handled by http-acceptor acceptor
11:44:29,282 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221007: Server is now live
11:44:29,283 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 56) HQ221001: HornetQ Server version 2.4.5.FINAL (Wild Hornet, 124) [f94a27d0-cbd0-11e4-8360-25874cd3b289] 
11:44:29,318 INFO  [org.jboss.as.messaging] (ServerService Thread Pool -- 56) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory
11:44:29,340 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 59) HQ221003: trying to deploy queue jms.queue.DLQ
11:44:29,352 INFO  [org.jboss.as.messaging] (ServerService Thread Pool -- 59) JBAS011601: Bound messaging object to jndi name java:/jms/queue/DLQ
11:44:29,354 INFO  [org.jboss.as.messaging] (ServerService Thread Pool -- 57) JBAS011601: Bound messaging object to jndi name java:/ConnectionFactory
11:44:29,354 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 58) HQ221003: trying to deploy queue jms.queue.ExpiryQueue
11:44:29,356 INFO  [org.jboss.as.messaging] (ServerService Thread Pool -- 58) JBAS011601: Bound messaging object to jndi name java:/jms/queue/ExpiryQueue
11:44:29,429 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-8) JBAS010406: Registered connection factory java:/JmsXA
11:44:29,493 INFO  [org.hornetq.ra] (MSC service thread 1-8) HornetQ resource adaptor started
11:44:29,494 INFO  [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-8) IJ020002: Deployed: file://RaActivatorhornetq-ra
11:44:29,499 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-7) JBAS010401: Bound JCA ConnectionFactory [java:/JmsXA]
11:44:29,500 INFO  [org.jboss.as.messaging] (MSC service thread 1-7) JBAS011601: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory
11:44:29,578 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015961: Http management interface listening on http://127.0.0.1:9990/management
11:44:29,579 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015951: Admin console listening on http://127.0.0.1:9990
11:44:29,580 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: WildFly 8.2.0.Final "Tweek" started in 6546ms - Started 218 of 267 services (93 services are lazy, passive or on-demand)

Still in the bin directory, the second thing to do, if you still don't have a user is to create one. For this we run the add-user.sh command (in windows the extension is .bat). Beware of one detail: the password you enter here will imply changes to the code below. I used the password "br1o+sa*" (without the quotes), if you don't want to change any source code:

bin filipius$ ./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : joao
Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.
 - The password should not be one of the following restricted values {root, admin, administrator}
 - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
 - The password should be different from the username
Password : 
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: guest
About to add user 'joao' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'joao' to file '/opt/wildfly-8.2.0.Final/standalone/configuration/application-users.properties'
Added user 'joao' to file '/opt/wildfly-8.2.0.Final/domain/configuration/application-users.properties'
Added user 'joao' with groups guest to file '/opt/wildfly-8.2.0.Final/standalone/configuration/application-roles.properties'
Added user 'joao' with groups guest to file '/opt/wildfly-8.2.0.Final/domain/configuration/application-roles.properties'
Is this new user going to be used for one AS process to connect to another AS process? 
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? no

We now need to create a queue, if we are going to use it. One might edit the configuration file standalone-full.xml directly, but it is possibly simpler to use the command line interface, as follows (note that this only works if the server is running):

bin filipius$ ./jboss-cli.sh 
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands.
[disconnected /] connect
[standalone@localhost:9990 /] jms-queue add --queue-address=PlayQueue --entries=java:jboss/exported/jms/queue/PlayQueue
[standalone@localhost:9990 /]

The server outputs the following message:

14:22:06,037 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 64) HQ221003: trying to deploy queue jms.queue.PlayQueue
14:22:06,038 INFO  [org.jboss.as.messaging] (ServerService Thread Pool -- 64) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/queue/PlayQueue

If you need to remove this queue just do jms-queue remove --queue-address=PlayQueue. We now may see the result of this action in the file standalone-full.xml directly (look for this file in the directory standalone/configuration). The entry "PlayQueue" was not there before we added it. You can take my word on this.

<jms-destinations>
   <jms-queue name="ExpiryQueue">
      <entry name="java:/jms/queue/ExpiryQueue"/>
   </jms-queue>
   <jms-queue name="DLQ">
      <entry name="java:/jms/queue/DLQ"/>
   </jms-queue>
   <jms-queue name="PlayQueue">
      <entry name="java:jboss/exported/jms/queue/PlayQueue"/>
   </jms-queue>
</jms-destinations>

The Sender

We are now going to write three applications, a sender and two receivers that use this queue. As usual, I'm assuming that we are using Eclipse. Let us start by creating a standard Java project:



and name it BasicJMS2. I'm using Java 8:



We may now create the Sender (File-->New-->Class):

with the following contents:

import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSContext;
import javax.jms.JMSProducer;
import javax.jms.JMSRuntimeException;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Sender {
private ConnectionFactory cf;
private Destination d;

public Sender() throws NamingException {
this.cf = InitialContext.doLookup("jms/RemoteConnectionFactory");
this.d = InitialContext.doLookup("jms/queue/PlayQueue");
}

private void send(String text) {
try (JMSContext jcontext = cf.createContext("joao", "br1o+sa*");) {
JMSProducer mp = jcontext.createProducer();
mp.send(d, text);
} catch (JMSRuntimeException re) {
re.printStackTrace();
}
}

public static void main(String[] args) throws NamingException {
Sender s = new Sender();
s.send("Hello Receiver!");
}
}


What is different comparing to this? The Connection and the Session objects are gone! We now have a JMSContext instead, which replaces both for most cases (actually, the Connection and Session still exist and are not deprecated, but they were - are! - much more complicated to use). One interesting detail is that we do not need to explicitly close the JMSContext using this try-with-resources block. It closes automatically when the block finishes. This will certainly save a lot of time for inexperienced users that forget to close resources. This would make applications unable to receive messages from a specific queue for a while in the subsequent tries. I was used to see students wasting their time looking for non-existing bugs, when the only problem was a missing close(). The new JMSContext still needs a username and a password, but we've already gone through the creation of the user before. Don't forget to use your own password!

The JMSProducer replaces the MessageProducer with another simplification in the message send.

This said, you should not get a very impressive result:


We need to add a library that exists in WildFly: the jboss-client.jar in the directory bin/client. For this, we first use the contextual menu over the name of the project, and then go to the option Build Path-->Configure Build Path. Pick the Libraries tab and navigate to the jboss-client.jar. Once you add it, the red underlines should disappear.



The Synchronous Receiver

Let us now write the synchronous Receiver:

import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSRuntimeException;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Receiver {
private ConnectionFactory cf;
private Destination d;

public Receiver() throws NamingException {
this.cf = InitialContext.doLookup("jms/RemoteConnectionFactory");
this.d = InitialContext.doLookup("jms/queue/PlayQueue");
}
private String receive() {
String msg = null;
try (JMSContext jcontex = cf.createContext("joao", "br1o+sa*");) {
JMSConsumer mc = jcontex.createConsumer(d);
msg = mc.receiveBody(String.class);
} catch (JMSRuntimeException re) {
re.printStackTrace();
}
return msg;
}

public static void main(String[] args) throws NamingException {
Receiver r = new Receiver();

String msg = r.receive();
System.out.println("Message: " + msg);
}


}


And we need an extra file: jndi.properties. This file contains the properties for Java Naming and Directory Interface (JNDI), which we use to lookup the ConnectionFactory and the Destination in the constructors of both examples:

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

To create this file just select the same folder of Receiver. java and Sender.java, right-click and pick (New-->File). This is the tree structure we get in the end:



You can now run the receiver and the sender in either order (select the File and go to the Run Menu). When you finish running both applications, the console should show the following on the receiver side:

Mar 16, 2015 3:05:58 PM org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.0.Final
Mar 16, 2015 3:05:58 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.0.Final
Mar 16, 2015 3:05:58 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 4.0.6.Final
Mar 16, 2015 3:06:00 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
Mar 16, 2015 3:06:00 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@68c4039c, receiver=Remoting connection EJB receiver [connection=Remoting connection <57577092>,channel=jboss.ejb,nodename=filipius-portatil]} on channel Channel ID c21f8d4f (outbound) of Remoting connection 2b568ca9 to localhost/127.0.0.1:8080
Mar 16, 2015 3:06:00 PM org.jboss.ejb.client.EJBClient <clinit>
INFO: JBoss EJB Client version 2.0.1.Final
Message: Hello Receiver!

The Asynchronous Receiver

Let us now write the asynchronous receiver:

import java.io.IOException;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class ReceiverAsync implements MessageListener {
private ConnectionFactory cf;
private Destination d;

public ReceiverAsync() throws NamingException {
this.cf = InitialContext.doLookup("jms/RemoteConnectionFactory");
this.d = InitialContext.doLookup("jms/queue/PlayQueue");
}
@Override
public void onMessage(Message msg) {
TextMessage tmsg = (TextMessage) msg;
try {
System.out.println("Got message: " + tmsg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
public void launch_and_wait() {
try (JMSContext jcontext = cf.createContext("joao", "br1o+sa*");) {
JMSConsumer consumer = jcontext.createConsumer(d);
consumer.setMessageListener(this);
System.out.println("Press enter to finish...");
System.in.read();
} catch (JMSRuntimeException | IOException re) {
re.printStackTrace();
}
}

public static void main(String[] args) throws NamingException {
ReceiverAsync r = new ReceiverAsync();
r.launch_and_wait();
}



}


We can now run it. Differently from the synchronous case, this receiver keeps reading the messages that come into the queue, until the user presses enter. Notice that we do not need to create any thread, as the JMS library will create one for you (most likely in the setMessageListener() invocation). This thread, however, cannot survive the end of the JMS context where it lives. For this reason, we must wait for the enter key inside the try-with-resources block.

In the following example, I ran the Sender three times, before finishing the receiver.

Mar 16, 2015 8:54:29 PM org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.0.Final
Mar 16, 2015 8:54:29 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.0.Final
Mar 16, 2015 8:54:29 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 4.0.6.Final
Mar 16, 2015 8:54:30 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
Mar 16, 2015 8:54:30 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@6f7fd0e6, receiver=Remoting connection EJB receiver [connection=Remoting connection <7604159b>,channel=jboss.ejb,nodename=ovelha]} on channel Channel ID d9bffeb7 (outbound) of Remoting connection 2ef23fb4 to localhost/127.0.0.1:8080
Mar 16, 2015 8:54:30 PM org.jboss.ejb.client.EJBClient <clinit>
INFO: JBoss EJB Client version 2.0.1.Final
Press enter to finish...
Got message: Hello Receiver!
Got message: Hello Receiver!
Got message: Hello Receiver!

That's it. Long live JMS 2.0!