Showing posts with label tech notes. Show all posts
Showing posts with label tech notes. Show all posts

Sunday, 10 November 2013

Sending and receiving a message using JMS 2 API

Sending a message with JMS 2.0
The JMS 2.0 aka simplified API has reduced the number of JMS objects required to send a message to a destination. The simplified API provides two objects for the said task, JMSProducer and JMSConsumer interfaces respectively. The good thing about them is they both allow method calls to be chained together. 

Let's take a look at the sender code. 

public void sendMessage(String messageText, String destinationAddress){
   ResourceFactory resourceFactory = new ResourceFactoryImpl();
   try(JMSContext jmsContext = resourceFactory.getJMSContext(JMSContext.AUTO_ACKNOWLEDGE);){
Queue queue = jmsContext.createQueue(destinationAddress);
jmsContext.createProducer().send(queue, messageText);
   }
}


A word on JMSContext.createQueue(String queueAddress)
The JMSContext.createQueue(String queueAddress) would not create a physical destination but Queue object for an existing destination. It is encouraged to look up for the administrative Queue object on the JNDI namespace since this approach reduces the chances of portability of the code as we have to provide it with a vendor specific physical Queue name. 

However, in this example, it's required to provide this method with the physical queue name. In HornetQ, it's the queue address name.

On a side note, the JMS API does not provide a mechanism to create permanent destinations. It's up to the JMS implementation to provide a mechanism to create permanent destination(s). This is not mandatory since JMS specification doesn't impose JMS providers to implement such a feature.

However the JMS API allows the creation of temporary destinations. The temporary destination(s) would be removed and garbage collected when the JMS Session/JMSContext associated with the temporary destination gets closed. 


Receiving a message with simplified API 
The JMS receiver code is pretty simple too. One thing you would notice is the ease of obtaining the message payload using JMSConsumer.receiveBody(), without performing any typecasting as it was with the JMS 1.1 specification. Here's the receiver code :

public String receiveTextMessage(String destinationAddress){
   ResourceFactory resourceFactory = new ResourceFactoryImpl();
   try(JMSContext jmsContext = resourceFactory.getJMSContext(JMSContext.AUTO_ACKNOWLEDGE);){
      Queue queue = jmsContext.createQueue(destinationAddress);
      JMSConsumer jmsConsumer = jmsContext.createConsumer(queue);
      return jmsConsumer.receiveBody(String.class);
   } 
}



The ResourceFactory helper interface and its implementation excerpt :

public interface ResourceFactory {
   public ConnectionFactory getConnectionFactory();
   public JMSContext getJMSContext(int sessionTx);
}


public class ResourceFactoryImpl implements ResourceFactory {
....
....
    @Override
    public JMSContext getJMSContext(int sessionTx) {
       ConnectionFactory connectionFactory = getConnectionFactory();
       JMSContext jmsContext = connectionFactory.createContext(JMSHelper.getProperty("jms.user.name"), JMSHelper.getProperty("jms.user.password"),sessionTx);
       return jmsContext;
    }
....
....
}

Thursday, 31 October 2013

JMS Connection Factories Explained

What ConnectionFactory should I use in my application ?

I thought it would be good to explain a bit about connection factories before I go on explaining JMS 2.0. The connection factories are one of the two administrative objects offered by JMS API. Both JMS 1.1 and 2.0 support the generic ConnectionFactory. The connection factories support concurrent use just like the Destination administrative object. There are domain specific connection factories in JMS 1.1 spec and they remain unchanged in 2.0. I personally discourage the use of domain specific connection factories just as it was said in the spec. The WildFly application server provides four connectionfactory instances on its default JMS provider, HornetQ's configuration :

<jms-connection-factories>
   <connection-factory name="InVmConnectionFactory">
      <connectors>
         <connector-ref connector-name="in-vm"/>
      </connectors>
      <entries>
         <entry name="java:jboss/DefaultJMSConnectionFactory"/>
         <entry name="java:/ConnectionFactory"/>
      </entries>
   </connection-factory>
   <connection-factory name="ServletConnectionFactory">
      <connectors>
         <connector-ref connector-name="servlet"/>
      </connectors>
      <entries>
         <entry name="java:jboss/exported/jms/ServletConnectionFactory"/>
      </entries>
   </connection-factory>
   <connection-factory name="RemoteConnectionFactory">
      <connectors>
         <connector-ref connector-name="netty"/>
      </connectors>
      <entries>
         <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
      </entries>
     <confirmation-window-size>100</confirmation-window-size>
   </connection-factory>
   <pooled-connection-factory name="hornetq-ra">
      <transaction mode="xa"/>
      <connectors>
         <connector-ref connector-name="in-vm"/>
      </connectors>
      <entries>
         <entry name="java:/JmsXA"/>
      </entries>
   </pooled-connection-factory>
</jms-connection-factories>

The connection factory instances differentiate by their characteristics. I would rather explain in terms of transport layer and on application type to aid what instance to be used in a client application. 

Remote JMS clients :
I would recommend the factory bound at jms/RemoteConnectionFactory or jms/ServletConnectionFactory for remote JMS clients. The latter uses servlet transport over the configured http port where as the former uses netty on port 5445. The http bound connection factory resolves the need of opening a port on the firewall.

Clients inside the same container :
The clients who reside the in the same container should obtain connections from the JCA based connection factory; from the factory bound at java:/JmsXA. It is considered an anti-pattern to obtain connections other than the factory bound at java:/JmsXA . When you obtain connections from this factory, you're indeed obtaining connections from the ManagedConnectionPool (MCP).

When you obtain connections from the factory bound at java:/JmsXA, it's not only just a method call but the connection objects support two phase commit protocol. Hence, they can be enlisted in a global transaction. Further you're obtaining connections from JCA's MCP,  with the container managed security. 

However there's a limitation imposed by the JEE 7 specification, under the section 6:7. It does not permit EJB/Web applications to have more than a single active session from a connection obtained from the MCP. This restriction does not encourage JMS clients to use a JVM based connection factory inside the container, which is meant for legacy JMS applications. Instead, the users must make sure to reuse JMS objects efficiently since JMS objects are reusable.

The purpose of using a JMS resource adapter is compromised and nullified when JMS clients obtain connections from anywhere other than the managed connection pool; when they JMS clients are inside the same container. It is a bad practice use any connection factory than the JCA based factory.

The JMS ConnectionFactory and Connection objects can be concurrently accessed and reusable. The plain JMS clients must make sure to cache and reuse connection objects instead of obtaining a connection at each message send. Although session objects are reusable, I would not encourage them to be shared across threads, to assure thread safety. Further, it might have an adverse impact on transacted sessions. However, you don't have to cache JMS resources inside the container, the container does this for you when you obtain connections from the MCP.

Saturday, 14 September 2013

disclaimer

I thought it would be good to maintain a separate blog to capture my tech notes. Although I maintain a blog on life, outside the world dominated by zeroes and ones. This would capture my day to day technical bits.

I'm not certain I'm writing this blog for the world outside. I truly want to track and note the important things I do for my own references, a journal. That said, I would keep this blog open so the others might benefit with what I write, if that's so. I'm still not sure on the latter though. 

The views/ideas/thoughts expressed on this blog are mine alone. All are my utmost personal thoughts and views and do not reflect the views of my employer or its affiliated entities.


Creative Commons License
This blog by Tyronne W is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
Based on a work at http://jmsrar.blogspot.com.au/.