Monday, February 23, 2009

Security Made Easy

This article introduces some Java security API usage and provides simple solutions and code samples to solve common messaging security issues. These issues are becoming more important when programming in the web/cloud environment.

The first thing many of us would think of when talking about communication security is to use SSL/TLS/HTTPS technology. And in deed, this probably is the most popular mechanism used in today's enterprise and web applications.

The SSL/HTTPS provides a secure communication channel to authenticate the server and/or client (if client also presents its own certificate). If successfully connected (authenticated), the information (messages) transmission in between the two communication parties can not be read (privacy) or modified (integrity).

The technology is well established today. Tutorials and code samples can be obtained from many related books or articles. So we will not repeat the topic here. For example, the following link provides useful references for Java in this topic.

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html


As the messaging providers move to the web (or cloud), SSL/HTTPS may not be enough to address all the security requirements. For example, messages received by a service may be stored in a file or a database until consumed (or removed at a later time).

The information may be compromised, if not protected, while it is stored at the persistent storage.

The rest of the article addresses the issue with a famous "Bob and Alice" security example as described below:

Bob wanted to send a secret message to Alice with the following criteria.
  • Alice can validate that the message is authentic (authored by Bob).
  • Alice can validate that the message is not tampered (while persisted in store).
  • Only Bob and Alice can understand (decrypt) the content of the message.
A simple solution for the above requirement is to use the public-key infrastructure and a hybrid cryptosystem.
(http://en.wikipedia.org/wiki/Hybrid_cryptosystem)

Before a message is sent, Bob does the following to sign and encrypt the message.
  • Generate a SecretKey (symmetric key) and encrypt the message with the SecretKey. The encrypted message is a byte[] (encryptedMessage).
  • Sign the (clear) message with Bob's private key. The signature is a byte[] (messageSignatureFromBob).
  • Encrypt the secret key with Alice's public key. The encrypted secret key is a byte[] (encryptedSecretKey).
The BOLD information above are to be sent to Alice as the "Secured Message". In other words, the message to be sent contains at least the three components: (encryptedMessage), (messageSignatureFromBob), and (encryptedSecretKey).

When a "Secured Message" is received, Alice does the following to verify the signature and decrypt the message.
  • Decrypt the encryptedSecretKey with Alice's private key. Alice reconstructs the SecretKey after the key is decrypted.
  • Decrypt the encryptedMessage with the reconstructed SecretKey.
  • Verify the messageSignatureFromBob with Bob's public key.
The following Java code samples exercised the above Alice/Bob message exchange scenario.

0. Certificate configurations. Bob and Alice each has its own certificate (and its own private key). Bob and Alice also obtained each other's certificate in their keystore.

Below are examples to create a self-signed certificate, import, and export certificates.

0.1 Example to create a self-signed certificate for bob.

keytool -genkeypair -dname "cn=bob, ou=em, o=em, c=us" -alias bob -keyalg RSA -keypass bobpass -keystore /keystores/bob/keystore -storepass bobpass -validity 180
Note: The -keyalg option is used because the default uses DSA algorithm,
but the DSA key algorithm is not supported in Sun's JDK 1.6.x Cipher implementation.

0.2 Example to export bob's certificate:

keytool -exportcert -alias bob -keystore /keystores/bob/keystore -file bob.cer
0.3 Example to import Alice's certificate into Bob's keystore:

keytool -keystore /keystores/bob/keystore -importcert -file alice.cer -storepass bobpass

Alice would do similar certificate setups as Bob's configurations above.

Link to Java keytool usage:
http://java.sun.com/javase/6/docs/technotes/tools/windows/keytool.html


1. Bob does the following to generate the SecretKey, sign the message, and encrypt the message.

1.1 Obtain Alice's public key.

private static final String BobKeyStorePath = "/keystores/bob/keystore";

//get bob's keystore password
byte[] bobpass = getBobPasword();

//create key store instance
java.security.KeyStore bobKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
java.io.FileInputStream fis = new java.io.FileInputStream(BobKeyStorePath);

//load the key store
bobKeyStore.load(fis, bobpass);

//get alice's certificate
Certificate aliceCert= bobKeyStore.getCertificate("alice");

//get alice's public key
java.security.PublicKey alicePublicKey = aliceCert.getPublicKey();

1.2 Generate a secret key (symmetric key)

//generate a secret key with triple-DES algorithm
javax.crypto.KeyGenerator keygen = KeyGenerator.getInstance("DESede");

//this is used to encrypt msg
javax.crypto.SecretKey secretKey = keygen.generateKey();

1.3 Encrypt the SecretKey with Alice's public key.

//get a new cipher instance with RSA as the transformation name
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, alicePublicKey);

//this is the encrypted secret key
byte[] encryptedSecretKey = cipher.doFinal(secretKey.getEncoded());

1.4 Sign the message.

The signature process is to ensure that a message received by Alice was indeed sent by Bob. The signature also serves the purpose that Bob can not deny that he did not send the message.

//get bob's private key entry
KeyStore.PasswordProtection kp = new KeyStore.PasswordProtection (bobpass);
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) bobKeyStore.getEntry("bob", kp);

//get bob's private key
java.security.PrivateKey bobPrivateKey = pkEntry.getPrivateKey();

//get a signature object for bob
java.security.Signature bobSignature = Signature.getInstance("SHA1withDSA");

//initialize the signature object with bob's private key,
//it is now ready to use
bobSignature.initSign(bobPrivateKey);

//message to be sent to Alice
String message = "message to Alice";

//encode the clear message with "utf8" encoding
byte[] data = message.getBytes("utf8");

//update data to signed by bob's signature object
bobSignature.update(data);

//sign the data.
byte[] messageSignatureFromBob = bobSignature.sign();

1.5. Encrypt the message using the secret key generated.

//get a cipher object with triple-DES algorithm
Cipher cipher = Cipher.getInstance("DESede");

//initialize the cipher object with geberated secret key (symmetric key)
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

//encrypt the message
byte[] encryptedMessage = cipher.doFinal(data);


2. Alice does the following to verify the signature, decrypt the SecretKey, and decrypt the message.

2.1 Obtain Alice's private key.

private static final String AliceKeyStorePath = "/keystores/alice/keystore";
//get alice's keystore password
byte[] alicepass = getAlicePasword();

//create key store instance
java.security.KeyStore aliceKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
java.io.FileInputStream fis = new java.io.FileInputStream(AliceKeyStorePath);

//load the key store
aliceKeyStore.load(fis, alicepass);

//get alice private key entry
KeyStore.PasswordProtection kp = new KeyStore.PasswordProtection (alicepass);
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) aliceKeyStore.getEntry("alice", kp);

//get alice's private key
java.security.PrivateKey alicePrivateKey = pkEntry.getPrivateKey();

2.2 Decrypt the SecretKey with alice's private key.

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, alicePrivateKey);

//this is to be sent to receiver along with the encrypted msg
byte[] encodedSecretKey = cipher.doFinal(encryptedSecretKey);

//build the key
SecretKey secretKey= new SecretKeySpec (encodedSecretKey, "DESede") ;

2.3 Decrypt the message with the SecretKey.

//decrypt data
Cipher cipher2 = Cipher.getInstance("DESede");
cipher2.init(Cipher.DECRYPT_MODE, secretKey);

//decrypt message
byte[] data = cipher2.doFinal(encryptedMessage );

String message = new String (data, "utf8");


2.4 Verify signature with Bob's public key.

//get Bob's certificate
Certificate certificate = aliceKeyStore.getCertificate("bob");

//get bob's public key
PublicKey publicKey = certificate.getPublicKey();

//get signature object instance
java.security.Signature signature = Signature.getInstance("SHA1withDSA");

//initialize with bob's public key
signature.initVerify(publicKey);

//update with data obtained in 2.2 (decrypted message)
signature.update(data);

//returns true if the signature is valid
boolean isValidSignature = signature.verify(messageSignatureFromBob );

Tuesday, February 17, 2009

Meet The Web, Part 2 (with transaction)

In the previous article "Meet The Web, RESTfully ", we introduced a conceptual model for a RESTful messaging service. In this article, we extended the interface and service to make a group of messaging operations in a transaction possible.

In the RESTful messaging service , the URLs to identify the message services are as follows.

http://www.effectivemessaging.com/jms/production/queue/destination_name
http://www.effectivemessaging.com/jms/consumption/queue/destination_name

Each HTTP protocol interaction (request-response) for the above service is essentially equivalent to a complete transaction. A client application examines the HTTP response code to verify if a message production or consumption request is successful.

At most one message can be in doubt (message in question) when a system failure occurred. In other word, when the application is unable to verify the HTTP response code from the response message, the status of the request is unknown.

This behavior is somewhat similar to a JMS application with a non-transacted (AUTO-ACKNOWLEDGE) JMS session. At most one message's status could be in question (unknown) when a system or network failure occurred.

In order to group a series of operations in a transaction, we define a set of "transacted protocols" to "link" these operations in a transaction.

The "transacted protocols" make it possible to commit or rollback a set of operations performed on the RESTful message service.

A conceptual implementation is described in the second section of this article.


1. Transacted protocols for the message service web interface.

The protocols includes setting the boundaries for a transaction (transaction demarcation). An unique TransactionID is created by the message service when a new transaction is created (START). Each operation in the transaction is then tagged with the TransactionID.

Applications may COMMIT a transaction after setting the END boundary. Applications may ROLLBACK a transaction if the transaction state is after START and before COMMIT.

1.0 Start a new transaction.

An application sends a HTTP request message to start a new transaction. If the request is accepted, a new TransactionID is set in the HTTP response message body and returned to the application.

URL:
http://www.effectivemessaging.com/transactions

Example request message:

----------------------------------------
POST /transactions HTTP/1.1
(other headers omitted)

TXCOMMAND=START
------------------------------------

The HTTP response message body contains a transaction ID generated by the message service.

Example response message:

--------------------------------------
HTTP/1.1 200 OK
(other headers omitted)

TransactionID=12345(UUID)
------------------------------------------

1.1 Produce and consume messages in a transaction.

The associated transaction ID is set in the HTTP header for the HTTP request message.

URL to send a message in the current transaction:
http://www.effectivemessaging.com/jms/production/queue/destination_name

Example request message:

-------------------------------------------------------------------------
POST /jms/production/queue/myQueue HTTP/1.1
Content-Type: text/plain;charset=UTF-8
User-Agent: Java/1.6.0_07
Host: localhost:8888
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 10
TransactionID: 12345 (uuid)

HelloWorld
--------------------------------------------------------------------------

Example response message:

--------------------------------------------------------------------------
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=UTF-8
Content-Length: 0
Date: Mon, 02 Feb 2009 21:59:58 GMT
TransactionID: 12345 (uuid)
---------------------------------------------------------------------------

URL to receive a message in the current transaction:
http://www.effectivemessaging.com/jms/consumption/queue/destination_name


Example consumption request message:

---------------------------------------------------------------------------
POST /jms/consumption/queue/myQueue/ HTTP/1.1
Content-Type: text/plain;charset=UTF-8
User-Agent: Java/1.6.0_07
Host: localhost:8888
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 0
TransactionID: 12345 (uuid)
---------------------------------------------------------------------------

Example consumption response message:

-----------------------------------------------------
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Mon, 02 Feb 2009 23:04:05 GMT
TransactionID: 12345 (uuid)

HelloWorld
-----------------------------------------------------

1.2 End a transaction.

http://www.effectivemessaging.com/trasnactions/12345(uuid)

Example request message:

-----------------------------------------------------
POST /transactions/12345(uuid) HTTP/1.1
(other headers omitted)
TransactionID: 12345 (uuid)

TXCOMMAND=END
-----------------------------------------

Example response message:

----------------------------------------------
HTTP/1.1 200 OK
(other headers omitted)
TransactionID: 12345 (uuid)
----------------------------------------------

1.3 Commit a transaction.

http://www.effectivemessaging.com/trasnactions/12345(uuid)

Example request message:

-----------------------------------------------
POST /transactions/12345(uuid) HTTP/1.1
(other headers omitted)
TransactionID: 12345 (uuid)

TXCOMMAND=COMMIT
------------------------------------------------

Example response message:

-----------------------------------------------
HTTP/1.1 200 OK
(other headers omitted)
TransactionID: 12345 (uuid)
-----------------------------------------------

1.4 Rollback a transaction.

http://www.effectivemessaging.com/trasnactions/12345(uuid)

Example request message:

-------------------------------------------------
POST /transactions/12345(uuid) HTTP/1.1
(other headers omitted)
TransactionID: 12345 (uuid)

TXCOMMAND=ROLLBACK
-------------------------------------------------

Example response message:

-------------------------------------------------
HTTP/1.1 200 OK
(other headers omitted)
TransactionID: 12345 (uuid)
-------------------------------------------


2. Conceptual transacted message service implementation.

The transacted protocols can be easily implemented (validated) if the RESTful messaging service is backed by a (JMS) service.

Upon received a transaction START request, the message service implementation can create a transaction object and an associated (JMS) transacted session. The transaction object contains the TransactionID as its look-up key and has a reference to the (JMS) transacted session.

The TransactionID is set in the HTTP START response message body and returned to the application.

For each sub-sequential transacted send/receive request (tagged with TransactionID), the message service delegate the request to the (JMS) transacted session associated with the transacted object. The transacted session is used to send and rereive transacted messages on behalf of the request.

The END protocol is defined here to make the interface uniform and extensible in the future (such as two-phase commit protocol support). It is also used to set the END transaction boundary precisely.

For a COMMIT/ROLLBACK request, the message service delegate the request to the JMS transacted session. The transaction is thus committed or rollback via Session.commit() or Session.rollback() if the request is validated.

Wednesday, February 11, 2009

Messaging In The Cloud

As Larry Ellison put it, "Software is a fashion business". Cloud computing is one of the hottest topics in the computer industry recently. As software developers that live with messaging, we would be "out of fashion" if we do not join the party in time.

With the concept of SaaS (Software As A Service) materialize, it is a natural evolution for many applications (software services) to be "hosted" in the "cloud". And also naturally, this includes the messaging services.

If you are new to the cloud computing terminology, here is a definition from wikipedia.

"Cloud computing is Internet ("cloud") based development and use of computer technology ("computing"), whereby dynamically scalable virtualised resources are provided as a service over the Internet." -- quoted from wikipedia:

http://en.wikipedia.org/wiki/Cloud_computing

In the previous article, the RESTful style of messaging had set the stage for the messaging service to be deployed in the cloud. The article titled "Meet The Web, RESTfully" discussed building a conceptual model for "messaging as a service":

(http://effectivemessaging.blogspot.com/2009/02/meet-web-restfully.html)

To enable the "messaging as a service" and deploy the service in the cloud, the (cloud) service provider would require to implement and deploy the message service in the provider's infrastructure.

Conceptually, the cloud service provider would provide the following services.
  • Cloud infrastructure. This includes hardware, operating systems, and required application stacks to host services.
  • Messaging service implementation. The services are available to the service consumer (authenticated and authorized) with public URIs. The service implementation may (optionally) include JMS message service as the back-end routing service.

The "cloud" concept could change the messaging development process in the corporate IT in a very fundamental way. The following issues are usually required to be included in the design and deployment for a messaging system.
  • Hardware issues.
  • Messaging server/service installation, configuration, upgrade, and maintenance issues.
  • Cluster and Highly-availability issues.
  • Scalability issues.
These issues are now owned by the cloud service provider if used.

IT software developers can simply use the new "web" API (such as the RESTful API mentioned in the previous article) to solve the messaging requirement between applications. The cloud service provider is responsible to have the messaging service "be available and be available all the time"!

This probably sounds too good to be true. And of course there are concerns with the new model:
  • The control (ownership) of the data (messages).
  • The security issues. Such as authentication, authorization, integrity, and privacy.
  • Is pay-per-use model right for you?
The control of the data issue is usually classified as business (corporate lawyer) issues. We will leave it to be solved by the business people.

The security issues can be resolved in the current available technology. Client applications could use https, certificates, and cryptographic libraries to resolve the mentioned security issues. The cloud service provider is also likely to have a security group to address the security concerns.

Messaging in the cloud is an attractive alternative for many scenarios. Such as for some start-ups and independent developers. New applications could be developed, tested and deployed immediately without all the up front hardware and software cost.

Potential cloud service providers or current messaging providers may roll out different levels of messaging service in the near future. In the mean while, Amazon already provides the "Simple Queue Service" (Amazon SQS) that address some of the "queue" messaging requirements.

In the end, it comes to the big question. Is the pay-per-use model right for you?

Monday, February 9, 2009

Meet The Web, RESTfully

This article introduces web interfaces to the JMS message services.

JMS has become a de facto reliable message service for the enterprise applications. As the train of applications has moved to the web, it is a natural requirement for the messaging service to be reachable from web applications. Or as a matter of fact, from any http capable applications.

A popular web interface is the RESTful style API. This article discusses the message service web interface in the context of the RESTful style API.

The "Representational state transfer (REST) is a style of software architecture for distributed hypermedia systems such as the World Wide Web." (Quoted from wikipedia) If you are new to the REST terminology, here is a link to a nice introduction to what it is.

http://en.wikipedia.org/wiki/Representational_State_Transfer

An usage example for the interface is to have AJAX applications (running in a browser) to send or receive messages to the message service. Another usage example of the interface is to have C# applications talk to Python applications asynchronously.

There are commercial (MOM) products that provide the interface and implementation. At the time of this writing, most of the implementation are in the transition to a more mature states. The following are some of the examples.

Apache Active MQ introduced a (accidental) RESTful API to its messaging service:
http://activemq.apache.org/rest.html

The recent release (MQ4.3) of Sun Java Message Queue introduced a more comprehensive web API and implementation:
https://mq.dev.java.net/4.3-content/ums/umsIntro.html


The following section presents a conceptual model (design and implementation overview) of a RESTful messaging service. This can be used as a reference to implement a simple RESTful message service to interface with your favorite messaging provider. The contents presented here assumed that the reader is familiar with basic HTTP protocol and JavaServlet Technology.

The model can be divided into two separate components. The first component is the interface (URI and protocol) to the service. The second component is the service implementation. Each component is elaborated in more details below.

1. The interface to the message service.

A RESTful MOM interface can be (naturally) modeled around Destinations. A destination is a resource that applications used to send or receive messages.

1.1 URL and protocol to send a message.

A message production service URI used to send messages to a destination can be designed as follows.

http://www.effectivemessaging.com/jms/production/queue/destination_name/

where "queue" in the URI identifies that the destination is a queue domain.
where "destination_name" is the name of the JMS destination in the messaging system.

For example, the following represents a protocol (interface) to send a message to the destination "myQueue" in the queue domain.

http://www.effectivemessaging.com/jms/production/queue/myQueue/

The HTTP method POST MAY be used for the "message production" protocol message.

The body of the HTTP message is the message body to be sent to the destination.

The "Content-Type" can be used to specify the content of the message.

The HTTP response status code is used to indicate the status of the request service.

An example protocol interaction (HTTP request-response message) to send a "HelloWorld" to destination "myQueue" would look like this:

----------------- HTTP Request ------------------
POST /jms/production/queue/myQueue/ HTTP/1.1
Content-Type: text/plain;charset=UTF-8
User-Agent: Java/1.6.0_07
Host: localhost:8888
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 10

HelloWorld

----------------- HTTP Response ---------------
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=UTF-8
Content-Length: 0
Date: Mon, 02 Feb 2009 21:59:58 GMT
-----------------------------------------------------

1.2. URL and protocol to receive a message.

A message consumption service used to receive messages from a destination can be designed as follows.

http://www.effectivemessaging.com/jms/consumption/queue/destination_name/

where "queue" in the URI identifies that the destination is a queue domain.
where "destination_name" is the name of the JMS destination in the messaging system.

For example, the following represents a protocol (interface) to receive a message from the destination "myQueue" in the queue domain.

http://www.effectivemessaging.com/jms/consumption/queue/myQueue/

The HTTP method POST MAY be used for the "receive" protocol message.

The received message body is set in the body of the HTTP response message.

The response message "Content-Type" can be used to specify the content of the response message.

The HTTP response status code is used to indicate the status of the request service.

An example protocol interaction (HTTP request-response message) to receive a message from destination "myQueue" would look like this:

----------------- HTTP Request --------------
POST /jms/consumption/queue/myQueue/ HTTP/1.1
Content-Type: text/plain;charset=UTF-8
User-Agent: Java/1.6.0_07
Host: localhost:8888
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 0

----------------- HTTP Response ---------------
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Mon, 02 Feb 2009 23:04:05 GMT

HelloWorld
---------------------------------------------------

2. Message Service Implementation.

One simple and straight forward implementation for the above service is to use the Java Servlet techonology. A servlet can be deployed in a container (such as Tomcat) to provide the send and receive services.

When the servlet is invoked, it obtains the service type (send or receive), destination domain (queue or topic), and destination name from the request URI. Theservlet then performs the messaging service on behalf of the request. The servelet may use cached Connections. The JMS Session/Producer/Consumer objects are created (or obtained if cached) to perform the requested services.

An appropriate status code and message body are returned to the client in the HTTP response message.

A special thanks to Keith Babo and Frank Kieviet at Sun Microsystems, Inc. for their input to the content of this article.






Friday, February 6, 2009

MDB Illustrated

In the previous article, we briefed the skeleton of a MDB. In this article, we describes a conceptional model to illustrate how MDB works inside a container.

A container is loosely referred to an application server in this article. The functions performed by a container mentioned below includes those done by a resource adapter and messaging provider.

When a MDB is deployed in a container, the container located two JMS resources that each MDB must be associated with.
  • The ConnectionFactory that the container uses to create a connection to the messaging server.
  • The Destination that the MDB is interested in receiving messages from the messaging server.
The container creates a connection (if none is available) to the messaging server based on the allocated ConnectionFactory. If the connection was acquired/created successfully, the container then creates a message consumer (on behalf of the MDB) on the destination associated with the MDB. The container is now ready to deliver messages to the MDB instances.

The container constructs a MDB instance with the following sequence.
  • Invokes the MDB class'es newInstance() method to obtain a new instance.
  • Injects resources to the instance,if any. For example, injects MessageDrivenContext if declared.
  • Invokes the PostConstruct method, if any.
When a message arrives (received by the container on behalf of the MDB), the container delivers/dispatches the message as follows.
  • Get an available MDB instance (or create a new one if none is available) from the instance pool.
  • Allocate a thread from the thread pool (to execute the MDB instance).
  • Start the allocated thread to deliver the message with the following sequence.
    1. Start a new transaction if the MDB uses a container-managed transaction (default).
    2. Invoke the onMessage(Message m) method.
    3. Acknowledge/commit the transaction when onMessage() returns.
    4. Return the current thread and MDB instance back to their own pools.
At any point in time, there could be multiple instances been executed concurrently per MDB class. The maximum concurrent instances are usually configured during deployment. The container executes each MDB instance with one thread at a time.

The container calls an MDB instance's PreDestroy method, if any, before it destroys the instance.

Thursday, February 5, 2009

MDB in 3 minutes

This article introduces basic JMS Message Driven Bean (MDB) design that uses EJB 3.0 API (Java EE 5).

A MDB (EJB 3.0 API) class can be constructed with the following skeleton.

1. Implement the javax.jms.MessageListener interface.

2. Annotated with the MessageDriven annotation.

3. Include supported dependency injection annotation (optional).

The following is a "functional" MDB code example.

--------------------------------------------------------------
package emessaging.mdb;

import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(mappedName = "jms/myMDBQ")
public class HelloWorldMDBBean implements MessageListener {

public HelloWorldMDBBean() {
}

public void onMessage(Message message) {
System.out.println("*** received message: " + message);
}

}

-----------------------------------------------------------------------------------

When deployed in a container that supports Java EE 5, the above MDB receives messages on destination with the JNDI loop up name "jms/myMDBQ". This MDB uses the container-managed transaction (default). Transaction and message acknowledgment are automatically handled by the container for each message delivered to aMDB instance (onMesage() invocation).

In order to have the above code working properly, the following resources are also required to be configured for the target container (and/or the messaging server).

  • A JNDI Destination object must be created with look up name "jms/myMDBQ".
  • The JMS destination resource associated with "jms/myMDBQ" must be created on the messaging server.
  • A JNDI ConnectionFactory object may required be created and associated with the MDB.

The ConnectionFactory and Destination objects are heavily vendor dependent. After they are configured, the information may also need to be packaged along with the MDB code and then deployed to a target container (app server).

It is an inhumanity process to configure/package the administrated objects/MDB without using a (vendor) supported IDE/tool.

At the time of this writing, the IDE/tools have been improved over the years and are reasonably useful for all major vendors.

For example, the above example can be constructed/deployed/tested with netbeans IDE without dealing with the details of the notorious Java EE configuration syntax.

If you are not comfortable with this example, here is a link to get familiar with the basics.

http://java.sun.com/javaee/5/docs/tutorial/doc/bnbpk.html

In order to be comfortable with the MDBs, you would probably like to know what happened "under the hood". In the coming article, we will introduce the details of how it works.

Tuesday, February 3, 2009

Unidentified Message Producer

The JMS Session is the factory to create message producers. When creating a message producer, the Session's create producer method parameter specifies a Destination object to be associated with the producer created.

The method in the Session API to create a message producer is as follows.
MessageProducer createProducer(Destination destination) throws JMSException;
If a non-null destination is specified in the parameter, the created message producer is called an identified message producer. An identified message producer is used when a producer always produces messages to the same destination during the life time of the producer object.

If a null destination is specified in the parameter, the created message producer is called an unidentified message producer. An unidentified message producer is used when a producer is used to produce messages to more than one destinations. For example, the following is an example to use an unidentified message producer. The send method in the code snippet sends a message to a queue or topic destination. The JMS objects are initialize (in the init() method) before the send method is invoked.

/**
* Initialize JMS objects.
*
* @throws JMSException
*/
public void init() throws JMSException {

//get an instance of JMS connection factory.
ConnectionFactory factory = EMFactory.getConnectionFactory();

//create a connection
conn = factory.createConnection();

//create a session
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

//create a message producer on the destination
producer = session.createProducer(null);

myTopic = session.createTopic("myTopic");

myQueue = session.createQueue("myQueue");
}

/**
* The send example method sends a message to a queue or topic based
* on the specified boolean parameter.
*
* @param sendToTopic true if the message is to be sent to topic
* destination. Otherwise, the message is sent to a queue destination.
*
* @param text The text message to be set in the text message.
*
* @throws JMSException if any internal error occurred.
*/
public void send(boolean sendToTopic, String text) throws JMSException {

// destination
Destination dest = null;

// create a queue instance
if (sendToTopic) {
dest = myTopic;
} else {
dest = myQueue;
}

// create a text message
TextMessage m = session.createTextMessage(text);

System.out.println("sending message to destination:\n " + dest);

// send the ping message
producer.send(dest, m);
}


An unidentified message producer is often used as a cached producer. Applications would get an instance of an unidentified message producer object from its cache implementation before sending a message. In this scenario, it is required to synchronize the usage of a cached producer. The messaging service behavior is undefined if a session (producer object) usage is not synchronized.

JMS ExceptionListener

The JMS API JavaDoc specified the ExceptionListener interface behavior as follows.

"If a JMS provider detects a serious problem with a Connection object, it informs the Connection object's ExceptionListener, if one has been registered. It does this by calling the listener's onException method, passing it a JMSException argument describing the problem.

An exception listener allows a client to be notified of a problem asynchronously. Some connections only consume messages, so they would have no other way to learn that their connection has failed.

A JMS provider should attempt to resolve connection problems itself before it notifies the client of them."

In practice, when an exception listener is called, the connection is broken and the JMS service is no longer available for the connection.

An ExceptionListener can be used for the following purposes.

1. This is the only standard API that a JMS message listener can be notified of a connection problem. When it is called, the JMS service is no longer available. An application can, at a minimum, log the problem and clean up its resources. Application can also notify any interest parties that needs to be notified of such a problem.

2. An application can, if necessary, implement its own simple high-availability functionality when an ExceptionListener is used. If an application is designed with a clean initialization setup, it would be feasible to reinitialize the JMS objects when the exception listener is called. The reinitialization process can even include (re)connecting to a list of pre-configured messaging servers. This simple JMS service-availability feature can be implemented with just a little effort.

An ExceptionListener should always be used in a JMS application, this is especially true for an application with only asynchronous message consumers.




Monday, February 2, 2009

JMS Reliability

This article revisited the JMS reliability and looked at the API provided parameters and their implications.

The JMS provides APIs to specify reliability for message producers and message consumers. The reliability consists of two separate contracts:
  • the contract between a producer and the messaging provider.
  • the contract between a consumer and the messaging provider.
The two contracts are essentially independent to each other. A message producer has no control over how a message is consumed and acknowledged. Similarly, a message consumer has no control over how messages are produced to a destination from which they are consumed.

To achieve end-to-end reliability, an application should be aware of both contracts and choose an appropriate configuration fits the application. The end-to-end reliability means the same message is produced exactly once and be received by a consumer exactly once.

1. Producing message reliably.

A message producer can specify the following parameters when producing a message to a destination. These parameters can affect message reliability if not used properly.
  • time-to-live
  • delivery-mode
  • message-priority
1.1 Time-To-Live.

The API Javadoc says as follows for the MessageProducer's setTimeToLive() API.

"Sets the default length of time in milliseconds from its dispatch time that a produced message should be retained by the message system."

For time-to-live to work reliably, a messaging provider may require to synchronize the clock among the messaging server and the messaging client runtime (applications). The implementation is vendor dependent and a vendor may choose not to synchronize the clock and assume this is a user's responsibility.

Applications should always use the default time-to-live value (0, never expire) if clock synchronization can not be guaranteed. Messages could be expired unexpected if clocks are not synchronized between client(s) and the messaging server(s).

1.2 Message Priority

Below is quoted from JMS API JavaDoc.

"The JMS API defines ten levels of priority value, with 0 as the lowest priority and 9 as the highest. Clients should consider priorities 0-4 as gradations of normal priority and priorities 5-9 as gradations of expedited priority. Priority is set to 4 by default."

In practice, the message priority should be treated as a hint only. Setting a high priority for a message may not guarantee the message be delivered before a lower priority message sent earlier. There are many factors may affect this. For example, a previous sent message (with lower priority) may already in transition to be delivered to a consumer. A high priority message produced at a later time may not be jump ahead and deliver to a consumer in a predictable way. The behavior is vendor dependent and should be used with caution.

1.3 Delivery Mode.

An application should produce non-persistent messages only if messages are allowed to be lost when a system failed (hardware or software).

An application should use a transacted session to produce persistent messages for maximum reliability. When Session.commit() is called, a messaging provider SHOULD sync the produced messages to its persistent repository to avoid message loss should a system failure occurred. It is consider a messaging provider's bug if a sync operation is not enforced when commit() returned successfully.

2. Consuming message reliably.

This section assumes that messages were produced reliably to a destination as discussed in the above section.

Message consumption reliability are controlled by the session acknowledge mode or if a transacted session is used.

A message lost is defined as follows.

"A message was produced reliably to a destination as per JMS specification (such as sending a persistent message to a queue in a transacted session) but was never seen by the message consumer attached to the same destination."

The reliability for various session acknowledge modes are discussed as follows.

2.1 asynchronous consumption (message listener) with auto-acknowledge session.

At most one message can be redelivered when a system failure occurred. No messages can be lost when a system failure occurred.

2.2 synchronous consumption with auto-acknowledge session.

According to the JMS reference implementation, at most one message can be lost when a system failure occurred.

2.3 dups-ok-acknowledge.

Multiple messages could be redelivered when a system failure occurred. At most one message could be lost for a synchronous receive consumer.

2.4 client-acknowledge session.

Messages consumed but not acknowledged are re-delivered when a system failure occurred. No messages can be lost when a system failure occurred.

2.5 transacted session.

Message consumed but not committed are re-delivered when a system failure occurred. No messages can be lost when a system failure occurred.

For a standard JMS implementation, transacted session and client-acknowledged session could have the same reliability. But when applications are deployed in a system with a highly-available feature, a transacted session could receive a better reliability due to the possibility that the provider may implement a 2-phase commit protocol for a transacted session. In this case, there would be less possibility for any message redelivery in a transacted session.