2012-09-21

Send your JMS messages to the Event Delivery Network (EDN) with a simple MDB


If you have read some of my previous articles, you should know that I’m using the SOA Suite on my project as a BPM, based on the coordination of JMS messages.
I naturally went for the solution proposed by Oracle : the JMS adapter. But it took me some time to figure out how to make it work :) The official documentation is complete, but when you need to really implement the concept, it’s often too light.
Then I got it working, as demonstrated in a previous blogpost but it was quite disappointing. In fact, I found it quite strenuous and complex. To me, the need for JCA (and deployment plan)  is not mandatory : I just wanted to KISS (keep it simple & stupid)
Then I started looking for some content on that and I found some interesting stuff on Edwin’s blog & Guido’s blog. But once again, I could not go with a complete implementation. Then I tried several things, tested and finally, I came up with an implementation that was satisfying (and which is used every single day in production).
To keep you from going the same loong path I went, here is my code :

package fr.mbutton.blog.mdb;

import java.io.StringWriter;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.UserTransaction;
import javax.xml.namespace.QName;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import oracle.fabric.blocks.event.BusinessEventConnection;
import oracle.fabric.blocks.event.BusinessEventConnectionFactory;
import oracle.fabric.common.BusinessEvent;
import oracle.integration.platform.blocks.event.BusinessEventBuilder;
import oracle.integration.platform.blocks.event.jms.JmsRemoteBusinessEventConnectionFactory;
import oracle.integration.platform.blocks.event.saq.SAQRemoteBusinessEventConnectionFactory;
import oracle.soa.common.util.XMLUtil;
import org.w3c.dom.Document;


@MessageDriven(mappedName = "your.topic.jndi.name",
   activationConfig = { 
      @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
      @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
      @ActivationConfigProperty(propertyName = "topicMessagesDistributionMode", propertyValue = "One-Copy-Per-Application") })
public class JMSBridgeToEDN implements MessageListener {

    // EDN JMS environment
    private final String connFactName   = "jms/fabric/EDNConnectionFactory";
    private final String xaConnFactName = "jms/fabric/xaEDNConnectionFactory";
    private final String queueName      = "jms/fabric/EDNQueue";

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
          TextMessage txt = (TextMessage) message;
          BusinessEventConnection connection = null;
          try {
            // You can choose between two methods : Oracle AQ or JMS
            // Default is Oracle AQ
            connection = getOracleAQConnection ();
            // But you can choose the other one :
            // connection = getWebLogicJMSConnection();
          
            // Publish to EDN
            BusinessEvent event = updateAndConvertBody(txt.getText());
            // System.out.println("Element : " + displayXMLDoc(event.getAsDoc()));
            connection.publishEvent(event, 3); // The number represents message priority (3 = default)
            } catch (Exception e) {
              e.printStackTrace();
            } finally {
              connection.close();
            }
        }
    }

    private BusinessEventConnection getWebLogicJMSConnection () throws Exception {
      InitialContext context = new InitialContext();
      UserTransaction userTransaction = (UserTransaction) context.lookup("javax.transaction.UserTransaction");
      QueueConnectionFactory queueConnectionFactory = ((QueueConnectionFactory)context.lookup(connFactName));
      QueueConnectionFactory xaQueueConnectionFactory = 
        ((QueueConnectionFactory)context.lookup(xaConnFactName));
      Queue jmsQueue = ((Queue)context.lookup(queueName));
    
      BusinessEventConnectionFactory factory =
          new JmsRemoteBusinessEventConnectionFactory(queueConnectionFactory,
                                                      xaQueueConnectionFactory,
                                                      jmsQueue,
                                                      userTransaction);
      
      return factory.createBusinessEventConnection();
 
    }
    
    private BusinessEventConnection getOracleAQConnection () throws Exception {
      InitialContext context = new InitialContext();
      UserTransaction userTransaction = (UserTransaction) context.lookup("javax.transaction.UserTransaction");
      
      // Working with both EDN datasources (as seen in class SAQRemoteBusinessEventConnectionFactory)
      DataSource ds = (DataSource) context.lookup("jdbc/EDNDataSource");
      DataSource localTxDs = (DataSource) context.lookup("jdbc/EDNLocalTxDataSource");
      
      BusinessEventConnectionFactory factory = 
        new SAQRemoteBusinessEventConnectionFactory(ds, localTxDs, userTransaction);
      
      return factory.createBusinessEventConnection();
    }

    private BusinessEvent updateAndConvertBody (String body) throws Exception {
      
      // [...]
      
      // Creation of the message to be sent over the EDN
      String TCEvenement = "TCEvenement";
      // A common mistake is to use the namespace of your message : IT'S NOT THE ONE YOU SHOULD USE !
      // You have to provide the namespace of your message as defined IN THE EDL FILE !
      String NameSpace = "http://fr.edf.doaat.coctos/events/edl/EventDefinition";
      BusinessEventBuilder builder = BusinessEventBuilder.newInstance();
      builder.setEventName(new QName(NameSpace, TCEvenement));
      builder.setBody(XMLUtil.parseDocumentFromXMLString(updatedBody).getDocumentElement());
      
      return builder.createEvent();
    }
    
    private String displayXMLDoc (Document doc) throws Exception {
      TransformerFactory transFactory = TransformerFactory.newInstance();
      Transformer transformer = transFactory.newTransformer();
      StringWriter buffer = new StringWriter();
      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
      transformer.transform(new DOMSource(doc), new StreamResult(buffer));
      return buffer.toString();    
    }
}

The method displayXMLDoc is completely optionnal : I used it to help me debug my code but that’s all.
Feel free to improve those codelines, as long as they help you, I’m glad.

Mots clés Technorati : ,,,


Useful links:
http://docs.oracle.com/cd/E21764_01/integration.1111/e10231/adptr_jms.htm
http://docs.oracle.com/cd/E21764_01/integration.1111/e10224/obe_intro.htm#BABHBGAG
http://guidoschmutz.wordpress.com/2010/01/12/using-the-event-api-to-publish-an-event-to-the-event-delivery-network-edn-the-spring-way/
http://biemond.blogspot.fr/2011/06/configure-en-test-jms-based-edn-in-soa.html

No comments: