Friday, October 26, 2012

Using a Message-Driven Bean in JBoss 7

Let me describe the environment where this example runs: I assume that we will deploy a EJB in JBoss 7, and this JBoss includes an implementation of a queueing system accessible via Java Message Service. Additionally, I assumes that we already configured a queue named queue/PlayQueue. Refer to my other message about the Java Message Service for details on how to configure such queue. Again, we need to start JBoss in the following way, to start the messaging system:


./standalone.sh --server-config=standalone-full.xml



We now can write the code of the Message Bean. It is actually quite simple. Pick the Java EE perspective on the upper right corner of Eclipse. Then, create a new EJB Project (File-->New menu). Here I only show the source code of the bean in a Java package named message. The most important and not-so-obvious details concern the annotation @MessageDriven:



package message;

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

/**
 * Message-Driven Bean implementation class for: MyBean
 *
 */
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/PlayQueue")
},
mappedName = "queue/PlayQueue")
public class MyBean implements MessageListener {

    /**
     * Default constructor. 
     */
    public MyBean() {
    }
/**
     * @see MessageListener#onMessage(Message)
     */
    public void onMessage(Message message) {
    try {
System.out.println("Recebi: " + ((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
    }

}

To export the project to JBoss we do as usual:

The project in the form of a .jar file goes to standalone/deployments directory of JBoss:

You can now check the output of JBoss to see if everything went smoothly.

And now we need a program to send a message:


import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException;


import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.jms.HornetQJMSClient;
import org.hornetq.api.jms.JMSFactoryType;
import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory;


public class Sender {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageProducer mp;

public Sender() throws NamingException, JMSException {
TransportConfiguration transportConfiguration =
new TransportConfiguration(NettyConnectorFactory.class.getName());              
cf = (ConnectionFactory) HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF,transportConfiguration);
c = cf.createConnection("joao", "pedro");
d = HornetQJMSClient.createQueue("PlayQueue");

this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
this.c.start();
this.mp = this.s.createProducer(this.d);
}

public void send(String contents) throws JMSException {
TextMessage m = this.s.createTextMessage();
m.setText(contents);
this.mp.send(m);
}

public void close() throws JMSException {
this.c.close();
}
public static void main(String[] args) throws NamingException, JMSException {
Sender s = new Sender();
s.send("Ola");
}

}

Once you execute this code you should get the following view or similar in JBoss' console:

Wednesday, September 26, 2012

A few variations over JMS

PREVIOUS NOTE: Although not deprecated, as this example should work just fine with Java EE 7, I would consider the asynchronous receiver example in this message to be obsolete. I would advise the reader to consider this message instead.

In this message, I will provide a few examples that slightly change a sender-receiver synchronous interaction. In the basic example, the sender just sends a simple message, "OH OH OH", and the receiver just prints it. You should notice that the message goes through the JBoss Message Provider. For these examples to run you need to register a user, set up the queue and start the provider, just as I showed in "Java Message Service with JBoss AS 7". We will change this basic example to create:

The Basic Case

To get a sender look here (and a synchronous receiver): Java Message Service with JBoss AS 7.

The Asynchronous Receiver

Let us now change the Receiver to get messages asynchronously. One should notice that the main thread needs to be put on halt, or otherwise it will leave and kill the daemon thread of the session that is waiting for the message. The main difference is that the Receiver class now implements the MessageListener interface. When you run this receiver you should press enter in the console to finish it. The sender goes unchanged.


import java.io.IOException;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Receiver2 implements MessageListener {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageConsumer mc;

public Receiver2() throws NamingException, JMSException {
InitialContext init = new InitialContext();
this.cf = (ConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao", "pedro");
this.c.start();
this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
mc = s.createConsumer(d);
mc.setMessageListener(this);
}

@Override
public void onMessage(Message msg) {
TextMessage tmsg = (TextMessage) msg;
try {
System.out.println(tmsg.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

private void close() throws JMSException {
this.c.close();
}


/**
* @param args
* @throws JMSException 
* @throws NamingException 
* @throws IOException 
*/
public static void main(String[] args) throws NamingException, JMSException, IOException {
Receiver2 r = new Receiver2();
System.in.read();
r.close();
}


}


ReplyTo and Temporary Destinations

Let us now play with the replyTo plus a temporary destination. The sender creates a queue where the receiver should send its reply to. I include only the code of the sender and the receiver that changes, starting from the sender.



Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer mp = s.createProducer(d);
TextMessage msg = s.createTextMessage();
Destination replyto = s.createTemporaryQueue();
msg.setJMSReplyTo(replyto);
msg.setText("OH OH OH");
mp.send(msg);
MessageConsumer mc = s.createConsumer(replyto);
TextMessage reply = (TextMessage) mc.receive();
System.out.println("Sender got back: " + reply.getText());
c.close();

The receiver:


Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer mc = s.createConsumer(d);
TextMessage msg = (TextMessage) mc.receive();
System.out.println(msg.getText());
TextMessage reply = s.createTextMessage(msg.getText() + " to you too my friend!");
MessageProducer mp = s.createProducer(msg.getJMSReplyTo());
mp.send(reply);

c.close();



Durable Subscriptions


You need to do a few changes to make a durable subscription. First, you need to give an id to the connection, as follows (only the second line is new):


c = cf.createConnection("joao""pedro");
c.setClientID("joao");

To create an appropriate message consumer you need to do as follows. This is pretty much the same as you used to do with a regular message consumer, except that you need to add a subscriptionid (a regular string):

MessageConsumer mconsumer = s.createDurableSubscriber(topic, subscriptionid);
This means that the clientid is independent of the identifier of the subscription (presumably, the client can have multiple durable subscriptions, although I didn't explicitly tried).

We also need to allow the role "guest" to make durable subscriptions. For this we edit the standalone-full.xml file (see my previous post). Note the lines with the "durable" word in it:

                <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"/>
                        <permission type="createDurableQueue" roles="guest"/>
                        <permission type="deleteDurableQueue" roles="guest"/>
                    </security-setting>
                </security-settings>

Now, just restart the JBoss AS 7.



Filtering


Let us again consider sending a message. The server can add a set*Property to the message to provide additional hints for the receiver to filter. Hence, just add a single line to the original sender:

TextMessage msg = s.createTextMessage();
msg.setText("OH OH OH");
msg.setLongProperty("Idade", 15);
mp.send(msg);

The receiver needs to add a filter like this, at the time it creates the consumer:

MessageConsumer mc = s.createConsumer(d, "Idade < 80");

You can now play with the ages on the sender and receiver sides. One obvious question is: when the receiver filters out the message, does the message provider keep it? Or should we lose the message for ever? E.g., assume that we don't care for any age above 45, but the sender sends a 46. Will we get this 46, once we change the receiving filter to 50?


Transactions


Finally the transactions, starting again from the server (go back to the original example, once again):


Session s = c.createSession(true, 0);
MessageProducer mp = s.createProducer(d);
TextMessage msg = s.createTextMessage();
msg.setText("OH OH OH");
mp.send(msg);
s.commit();
c.close();


The receiver:


Session s = c.createSession(true, 0); 
MessageConsumer mc = s.createConsumer(d);
TextMessage msg = (TextMessage) mc.receive();
System.out.println(msg.getText());
s.commit();
c.close();

To fully explore this example, you may want to do the following: delete the commit of the receiver and send one message. Run the receiver over and over again. It will always get the message. Restore the commit on the receiver and delete the commit on the server side. Now, you will see that the message never gets to the receiver.


Friday, May 11, 2012

Java Message Service with JBoss AS 7

PREVIOUS NOTE: Although not deprecated, as this example should work just fine with Java EE 7, I would consider this message to be obsolete. I would advise the reader to consider this message instead.

In this message, I will guide the reader through the process of writing a simple sender-receiver application that uses a Java Message Service queue for the JBoss Application Server 7.1. It may be my fault, but as the time of writing of this message, I failed to find good documentation on how to run the most basic example of JMS in JBoss AS 7. This is what I show here. I will assume that the reader is familiar with JMS. There are plenty of resources out there on the web covering this topic, and it is not the goal of this blog to do the same.

I tried all this on Mac OS X Lion with the Brontes version of JBoss.

The first thing to know is that by default JBoss doesn't start with messaging. Therefore, you must explicitly use a different configuration that enables messaging. In the standalone case, you can do as follows, but you may want to defer starting the server to a later stage, after performing all necessary changes I describe:

./standalone.sh --server-config=standalone-full.xml

In other words, you need to specify the configuration file standalone-full.xml. The bad news is that we will need to change this configuration file, but this is not particularly difficult.

But before going into the server details, let us focus on the client applications. I will create one sender and one receiver application. They are pretty basic, in the sense that they only exchange one message and finish. I present them here. You can create your own standard Java project in Eclipse (or NetBeans) and add these two classes to the default package:


import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Sender {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageProducer mp;

public Sender() throws NamingException, JMSException {
InitialContext init = new InitialContext();
this.cf = (ConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao", "pedro");
this.c.start();
this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
this.mp = this.s.createProducer(this.d);
}

private void send(String string) throws JMSException {
TextMessage tm = this.s.createTextMessage(string);
this.mp.send(tm);
}

private void close() throws JMSException {
this.c.close();
}

/**
* @param args
* @throws JMSException 
* @throws NamingException 
*/
public static void main(String[] args) throws NamingException, JMSException {
Sender s = new Sender();
s.send("Ola ca estou eu!");
s.close();
}


}

The Receiver:


import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Receiver {
private ConnectionFactory cf;
private Connection c;
private Session s;
private Destination d;
private MessageConsumer mc;

public Receiver() throws NamingException, JMSException {
InitialContext init = new InitialContext();
this.cf = (ConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao", "pedro");
this.c.start();
this.s = this.c.createSession(false, Session.AUTO_ACKNOWLEDGE);
mc = s.createConsumer(d);
}

private String receive() throws JMSException {
TextMessage msg = (TextMessage) mc.receive();
return msg.getText();
}

private void close() throws JMSException {
this.c.close();
}

/**
* @param args
* @throws JMSException 
* @throws NamingException 
*/
public static void main(String[] args) throws NamingException, JMSException {
Receiver r = new Receiver();

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

}


In this source code, what I found really problematic was to initialize the destination (the queue) and the ConnectionFactory. Eventually, I managed to use JNDI, as I should. I summarize the process in following lines:


InitialContext init = new InitialContext();
this.cf = (QueueConnectionFactory) init.lookup("jms/RemoteConnectionFactory");
this.d = (Destination) init.lookup("jms/queue/PlayQueue");
this.c = (Connection) this.cf.createConnection("joao""pedro");

We need the extra "jms" before the name of the resource. For this to work, we also need an extra file, the jndi.properties. Place this file in the src folder of your project (this will not work for WildFly. Check the new version in this message):


java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=remote://localhost:4447
jboss.naming.client.ejb.context=true
#username
java.naming.security.principal=joao
#password
java.naming.security.credentials=pedro




To use the JMS libraries (and other JBoss client libraries by the way) you need to add the following jar file: (jboss-home)/bin/client/jboss-client.jar.



Note that we are providing the (very weak) user credentials of "joao" with password "pedro" to create the connection. For this to work, we need to add this user in the server running the add-user.sh (or .bat) available in the bin directory. The script will let us know in exactly which files did it do the modifications, but this is for the sake of curiosity only. I show this process in the next figure.


For this example to work we also need to add the queue to the standallone-full.xml file (refer to the command line in the top).



                <jms-destinations>
                    <jms-queue name="testQueue">
                        <entry name="queue/test"/>
                        <entry name="java:jboss/exported/jms/queue/test"/>
                    </jms-queue>
                    <jms-queue name="PlayQueue">
                        <entry name="queue/PlayQueue"/>
                        <entry name="java:jboss/exported/jms/queue/PlayQueue"/>
                    </jms-queue>
                    <jms-topic name="testTopic">
                        <entry name="topic/test"/>
                        <entry name="java:jboss/exported/jms/topic/test"/>
                    </jms-topic>
                </jms-destinations>



Now, you should be able to start the server first and then run the receiver and the sender in any order. Good luck!

Wednesday, April 4, 2012

Creating an Enterprise Application Repository in Eclipse

In this message, I will create an Enterprise Application Repository Project with a WS, a JPA and an EJB project. I assume that you already know how to create a WS project. Here you can find how to create a JPA project (including a special persistence.xml file to deploy in JBoss 7).

Therefore, we start by the EJB project.


We need to add an EJB:


picking the following options:



and we insert the following code. Note the @Stateless and the @PersistentContext annotations. The latter will allow you to insert objects into the database, using the em.persist() method. The same for the queries:
package ejb;

import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import pacientes.Paciente;

import medicos.Medico;
import moradas.Morada;

@Stateless
public class EJBimplementation {
@PersistenceContext(name="Proj2-JPA")
EntityManager em;
public Medico[] listarMedicos() {
Query query = em.createQuery("from Medico m");
@SuppressWarnings("unchecked")
List<Medico> lstmedicos = query.getResultList();
return lstmedicos.toArray(new Medico[0]);
}
public void iniciarmedicos() {
Date d [] = { new GregorianCalendar(1980, 12, 23).getTime(), 
new GregorianCalendar(1960, 4, 12).getTime(), 
new GregorianCalendar(1970, 11, 3).getTime() };  
Medico [] medicos = { new Medico(d[0], "A Nibal"), 
new Medico(d[1], "Ra Miro"), 
new Medico(d[2], "Al Fredo") }; 
Morada [] moradas = { new Morada("R. Marques de Pombal, 428""3000-233""Coimbra"),
new Morada("R. Marques de Pombal, 430""3000-233""Coimbra"),
new Morada("R. Marques de Pombal, 432""3000-233""Coimbra")
};

for (int i = 0; i < medicos.length; i++) {
Medico m = medicos[i];
m.setMorada(moradas[i]);
em.persist(moradas[i]);
em.persist(m);
}
}

public Paciente[] listarPacientes() {
Query query = em.createQuery("from Paciente p");
@SuppressWarnings("unchecked")
List<Paciente> lstmedicos = query.getResultList();
return lstmedicos.toArray(new Paciente[0]);
}
public void iniciapacientes() {
Date d [] = { new GregorianCalendar(1980, 12, 23).getTime(), 
new GregorianCalendar(1960, 4, 12).getTime(), 
new GregorianCalendar(1970, 11, 3).getTime() };  
Paciente [] pacientes = { new Paciente(d[0], "A Nibal"), 
new Paciente(d[1], "Ra Miro"), 
new Paciente(d[2], "Al Fredo") }; 
Morada [] moradas = { new Morada("R. Marques de Marialva, 228""3000-233""Cantanhede"),
new Morada("R. Marques de Marialva, 230""3000-233""Cantanhede"),
new Morada("R. Marques de Marialva, 232""3000-233""Cantanhede")
};

for (int i = 0; i < pacientes.length; i++) {
Paciente m = pacientes[i];
m.setMorada(moradas[i]);
em.persist(moradas[i]);
em.persist(m);
}
}

}


Now, let's create the EAR project:


We assume that its name is DemoEAR and that we already have the three other projects (2nd figure):





In the web service project (Proj2-WS according to the example), we need to have something like this to refer to the EJB and to use the methods we defined in the EJB (note the correspondence):


package ejb

import javax.ejb.EJB;
import javax.jws.WebMethod;
import javax.jws.WebService;

import pacientes.Paciente;

import medicos.Medico;
import ejb.EJBimplementation;

@WebService
public class Proj2WS {
@EJB
private EJBimplementation ejb;

public Proj2WS() {
}

@WebMethod
public Medico[] listarMedicos() {
return ejb.listarMedicos();
}

@WebMethod
public void iniciarmedicos() {
ejb.iniciarmedicos();
}

@WebMethod
public Paciente[] listarPacientes() {
return ejb.listarPacientes();
}

@WebMethod
public void iniciarpacientes() {
ejb.iniciapacientes();
}
}


In this way, we refer to the JPA project in the EJB project, and from the WS project we use the EJB we built before. Assuming that all dependencies are satisfied we need the deploy the project as follows:


The entire work will be available as a web service. Just go to the JBoss management console.