In this post, I will explain how to set and access properties of a JMS messaages.
Note: I will be using JMS 2.0 api for this and future examples
Below is the complete Main code.
Main Class
1 package package2;
2
3 import java.util.Properties;
4
5 import javax.jms.ConnectionFactory;
6 import javax.jms.Destination;
7 import javax.jms.JMSException;
8 import javax.naming.Context;
9 import javax.naming.InitialContext;
10 import javax.naming.NamingException;
11
12 import package2.Consumer;
13 import package2.Producer;
14
15 public class Example2 {
16 public static void main(String[] args) throws NamingException, JMSException {
17 Properties env = new Properties();
18 env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
19 env.put(Context.PROVIDER_URL, "file:///C:/openmq5_1_1/temp");
20 InitialContext initialContext = new InitialContext(env);
21 ConnectionFactory connectionFactory = (ConnectionFactory)initialContext.lookup("MyConnectionFactory");
22 Destination destination = (Destination)initialContext.lookup("MyQueue");
23
24 Producer producer = new Producer(destination, connectionFactory);
25 Consumer consumer = new Consumer(destination, connectionFactory);
26
27 Thread producerThread = new Thread(producer);
28 Thread consumerThread = new Thread(consumer);
29
30 producerThread.start();
31 consumerThread.start();
32
33 initialContext.close();
34 }
35 }
At line 17 I have created Properties object to populate it with INITIAL_CONTEXT_FACTORY and PROVIDER_URL.
The value of these keys vary based on the JMS provider. So please consult your specific JMS provider for more details on this.
For the example I am using OpenMessageQueue 5.1 JMS provider.
At line 20 I created an InitialContext instance and at line 21 I created a ConnectionFactory instance.
At line 22 using the initialContext instance, I lookup for “Destination” instance with name “MyQueue”.
Destination class is a super class of “Queue” and “Topic” classes. Queue class represents the physical queue for point to point messaging, whereas Topic class represents the physical topic for publish-subscribe messaging.
My example follows point to point messaging.
At line 24 I created a “Producer” instance and at line 25 “Consumer” instance. Both the classes implements the Runnable interface. Producer will create and publish a message which will be consumed by Consumer.
To both instance I will pass the destination and connectionFactory instance.
The Destination class and ConnectionFactory are thread safe in other words they can be accessed by multiple threads.
At line 27 I associate “producer” instance with “producerThread” instance and at line 28 I associate “consumer” instance with “consumerThread”.
At line 30 and 31 I start the producerThread and consumerThread.
Now we will look into the Producer class. Below is the complete code of Producer class
Producer Class
1 package package2;
2
3 import javax.jms.ConnectionFactory;
4 import javax.jms.Destination;
5 import javax.jms.JMSContext;
6 import javax.jms.JMSProducer;
7 import javax.jms.Message;
8
9 public class Producer implements Runnable {
10 private Destination destination;
11 private ConnectionFactory connectionFactory;
12
13 public Producer(Destination destination, ConnectionFactory connectionFactory) {
14 this.destination = destination;
15 this.connectionFactory = connectionFactory;
16 }
17
18 @Override
19 public void run() {
20 JMSContext jmsContext = null;
21
22 try {
23 jmsContext = connectionFactory.createContext();
24 Message message = jmsContext.createTextMessage("Hello World");
25 message.setStringProperty("PropertyName", "PropertyValue");
26 JMSProducer jmsProducer = jmsContext.createProducer();
27 jmsProducer.send(destination, message);
28 } catch(Exception excep) {
29 excep.printStackTrace();
30 } finally {
31 if(jmsContext != null) {
32 jmsContext.close();
33 }
34 }
35 }
36 }
Properties can be used to add additional information to the messages. They are also used by message filters on the consumer side to filter messages.
The JMS api has several accessor and mutators for each datatype and for Object. The value of a property can be String, boolean, byte, double, int, short, long, or float.
They are three types of properties, which are
1) Application specific properties
2) JMS defined properties
3) Provider specific properties
JMS defined and Provider specific properties are added by the JMS provider, whereas Application specific properties are added by developers.
So in the above Producer code, I am setting a string property by calling “setStringProperty” on message instance. Refer to line 25.
Next I will show how to access them on the Consumer side. Below is the Consumer code
Consumer Class
1 package package2;
2
3 import javax.jms.ConnectionFactory;
4 import javax.jms.Destination;
5 import javax.jms.JMSConsumer;
6 import javax.jms.JMSContext;
7 import javax.jms.Message;
8 import javax.jms.MessageNotWriteableException;
9 import javax.jms.TextMessage;
10
11 public class Consumer implements Runnable {
12 private Destination destination;
13 private ConnectionFactory connectionFactory;
14
15 public Consumer(Destination destination, ConnectionFactory connectionFactory) {
16 this.destination = destination;
17 this.connectionFactory = connectionFactory;
18 }
19
20 @Override
21 public void run() {
22 JMSContext jmsContext = null;
23
24 try {
25 jmsContext = connectionFactory.createContext();
26 JMSConsumer jmsConsumer = jmsContext.createConsumer(destination);
27 Message message = jmsConsumer.receive();
28 System.out.println(((TextMessage)message).getText());
29 if(message.propertyExists("PropertyName")) {
30 System.out.println(message.getStringProperty("PropertyName"));
31 }
32
33 try {
34 message.setStringProperty("PropertyName", "Value");
35 } catch(MessageNotWriteableException excep) {
36 excep.printStackTrace();
37 }
38
39 message.clearProperties();
40 System.out.println("Property Exist: " + message.propertyExists("PropertyName"));
41 message.setStringProperty("PropertyName", "Valule");
42 System.out.println("Property Exist: " + message.propertyExists("PropertyName"));
43 jmsConsumer.close();
44 } catch(Exception excep) {
45 excep.printStackTrace();
46 } finally {
47 if(jmsContext != null) {
48 jmsContext.close();
49 }
50 }
51 }
52 }
At line 25 I create an instance of JMSContext.
Next we create an instance of JMSConsumer.
Next we receive the message by calling “receive” method on the jmsConsumer.
Once we receive the message, we check whether the property with name “PropertyName” exist or not using the method “propertyExists”. If exist then inside the if condition we call “getStringProperty” on message object and pass the property name as the argument. Refer to if condition at line 29.
Once the message properties are set and the message is sent, the properties will become read only. Any attempt to change the property values will throw “MessageNotWriteableException” exception. In the consumer code inside the try catch block (refer to line 33) I tried to change the value of the existing property. As a result the exception is thrown which I catch in the catch block and print the stack trace.
We can remove all the existing properties by calling “clearProperties” method on message object. Refer to line 39. Then we can add new properties. Refer to line 41.
Below is the output generated
Output
Hello World
PropertyValue
javax.jms.MessageNotWriteableException: [C4011]: Write message failed.
at com.sun.messaging.jmq.jmsclient.MessageImpl.checkAndSetProperty(MessageImpl.java:801)
at com.sun.messaging.jmq.jmsclient.MessageImpl.setStringProperty(MessageImpl.java:2106)
at package2.Consumer.run(Consumer.java:34)
at java.lang.Thread.run(Unknown Source)
Property Exist: false
Property Exist: true