Enterprise Java Bean - Container Agnostic with JMS Support
Purpose
To create a container-agnostic and Java Message Service- (JMS-)enabled Enterprise Java Bean (EJB) which wraps an instance of Erudine Behaviour Engine (EBE).
Theory
Erudine Behaviour Engine can be deployed in various ways. A common deployment scenario is using an EJB (3.0)-compliant Java Application Server (AS) and an Enterprise Java Bean (EJB) as described in the Enterprise Java Bean - Container Agnostic article. This example also focuses on the deployment of EBE as an EJB but uses JMS message queues as a source of input events (situations) and as a destination for processed data. Although this example utilises only a single message queue for the knowledge model input and output, it is most likely that multiple message queues will be required when working in the scope of an enterprise application. Like the Webservice-enabled EJB (Enterprise Java Bean - Container Agnostic with Webservice Support), this example utilises EJB 3.0 annotations which removes the need for a container-based XML configuration. The examples were created and tested using the JBoss Application Server in its version 4.2.3.GA.
| Note Before you begin working through this article, please ensure you have read the Enterprise Java Bean - Container Agnostic article and have a working knowledge in the following areas:
|
Actions
This action section consists of two parts:
- Creating a container-agnostic-enabled Enterprise Java Bean using JMS
- Creating an appropriate unit test.
Creating a container-agnostic-enabled Enterprise Java Bean using JMS
To create a container-agnostic EJB which represents an instance of Erudine Behaviour Engine and uses JMS message queues to retrieve situations and send its results:
- The EJB shown below is very similar to the one introduced in the Enterprise Java Bean - Container Agnostic with Webservice Support article. It also uses the EBE-POJO SimpleExecutorImpl, the utility class ExecutorResponse and the SchengenBorderControl model shipped with EBE.
package com.erudine.ebe.ejb.mdb; import javax.ejb.ActivationConfigProperty; import javax.ejb.EJBException; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import javax.naming.InitialContext; import com.erudine.ebe.Executor; import com.erudine.ebe.ExecutorResponse; import com.erudine.ebe.SimpleExecutorImpl; /** * Message Driven Bean that wraps an instance of the Behaviour Execution Engine * that allows you to process data through a knowledge model. * <p> * It listens to messages containing situation strings, on a predefined queue * and posts the results back onto another queue. * * @author Erudine * @version 1.00 * @since 1.00 */ @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName="destination", propertyValue="queue/ebeQueue"), @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue") } ) public class ExecutorMDBean implements MessageListener { Executor executor; Queue resultQueue = null; String resultQueueName = "queue/ebeResultQueue"; QueueConnection resultQueueConnection = null; QueueConnectionFactory queueConnectionFactory = null; public ExecutorMDBean() { try { InitialContext context = new InitialContext(); resultQueue = (Queue) context.lookup(resultQueueName); queueConnectionFactory = (QueueConnectionFactory) context.lookup("ConnectionFactory"); executor = new SimpleExecutorImpl("SchengenBorderControl", "Entrants"); } catch (Exception e) { throw new EJBException(e); } } public void onMessage(Message message) throws EJBException { try { TextMessage textMessage = (TextMessage) message; ExecutorResponse result = executor.execute(textMessage.getText()); sendMessage(result); } catch (JMSException e) { throw new EJBException(e); } } private void sendMessage(ExecutorResponse response) { try { resultQueueConnection = queueConnectionFactory.createQueueConnection(); QueueSession queueSession = resultQueueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); QueueSender queueSender = queueSession.createSender(resultQueue); ObjectMessage message = queueSession.createObjectMessage(); message.setObject( response ); queueSender.send(message); } catch (JMSException e) { throw new EJBException(e); } finally { if (resultQueueConnection != null) { try { resultQueueConnection.close(); } catch (JMSException e) {} } } } }
In contrast to the Webservice annotations of the EJB Webservice example, this EJB is annotated as being a message-driven bean (@MessageDriven) reading from the message queue ebeQueue. In its onMessage method, which is inherited from the JMS MessageListener interface, it processes the message text of a received message using the EBE executor and sends the result to the JMS message queue ebeResultQueue. - The deployment process for the EJB is exactly the same as described in the Enterprise Java Bean - Container Agnostic article.
Creating an appropriate unit test
To create a unit test which invokes the EBE-EJB using its JMS message queue:
- After the successful deployment of the EBE-EJB JAR file to the JBoss AS, you can write a simple JUnit (4.4) test which illustrates how the deployed version of EBE is used. The following @BeforeClass annotated method retrieves instances of the sender (sends a JMS message to the ebeQueue) and receiver (reads JMS messages from the ebeResultQueue) utility beans using the BeanFactoryLocator and BeanFactoryReference of the Spring Framework (http://www.springsource.com/products/enterprise).
... @BeforeClass public static void setUpInitialContextAndGetBean() throws NamingException { BeanFactoryLocator factoryLocator = SingletonBeanFactoryLocator.getInstance(); BeanFactoryReference factoryReference = factoryLocator.useBeanFactory("erudine"); senderMDB = (MessageSender) factoryReference.getFactory().getBean("sender"); receiverMDB = (MessageReceiver) factoryReference.getFactory().getBean("receiver"); } ... - The test itself first sends a JMS message containing the test situation to the input queue of the created EJB before it reads the result from the output queue of the same bean. It then validates the received conceptual graph with the expected value.
... @Test public void testMDBSendReceiveMessage() { String situation = getTestSituation(); senderMDB.sendMesage(situation); ExecutorResponse result = receiverMDB.recieveMessage(); String resultGraph = (String)result.getNodeList()[0].getGraphList()[0]; IGraph outputGraph = getGraphFromString(resultGraph); IGraph expectedGraph = getGraphFromString(getExpectedTestResult()); assertEquals( true, outputGraph.isIdentical(expectedGraph)); System.out.println("Message is valid !!"); } private String getTestSituation() { return "[Entrant: *Entrant]->(has)->[Surname: MUNSON*Surname]" + "[?Entrant]->(has)->[Forename: DEBORAH*Forename]" + "[?Entrant]->(has)->[Passport: 222952928*Passport]" + "[?Entrant]->(has)->[Visa: Long Stay Visa*Visa]" + "[?Entrant]->(has)->[Cash Carried: 3000*Cash Carried]" + "[?Entrant]->(has)->[Date of Birth: '10-1-1940'*Date of Birth]" + "[?Entrant]->(is)->[Spanish: *Spanish]" + "[?Entrant]->(has)->[Type: Woman*Type]" + "[?Entrant]->(has)->[comment: Woman*comment]" + "[?Entrant]->(has)->[name: 'Munson, D'*name]" + "[?Entrant]->(has)->[id: 0*id]"; } private String getExpectedTestResult() { return "[Entrant: *Entrant]->(is)->[Spanish: *Spanish]" + "[?Entrant]->(has)->[Cash Carried: 3000*Cash Carried]" + "[?Entrant]->(has)->[Visa: Long Stay Visa*Visa]" + "[State: 0*State1]->(has)->[Graph Not Found: *Graph Not Found]" + "[?Entrant]->(has)->[Name: *Name1]->(has)->[Surname: MUNSON*Surname]" + "[?Entrant]->(has)->[Passport: 222952928*Passport]" + "[Outcome: *Outcome]->(has)->[Finality: Allow Entry*Finality]" + "[?Entrant]->(has)->[Date of Birth: '10-1-1940'*Date of Birth]" + "[?Entrant]->(has)->[NSIS Person: 222952928*NSIS Person1]" + "[?Entrant]->(has)->[id: 0*id]" + "[?Name1]->(has)->[Forename: DEBORAH*Forename]"; } ...
Platform: all
EBE Version: 2.4
Category: Architecture and Deployment Guide
Author: Patrick Peisker