Enterprise Java Bean - Container Agnostic
Purpose
To create a container-agnostic 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). In order to deploy EBE to such an environment, it needs to be wrapped in an EJB. The following actions will guide you through the implementation and deployment of an EJB that contains an instance of EBE. Specifically, it shows how to deploy Erudine Behaviour Engine within a JBoss Application Server (4.2.3.GA) using a stateless Enterprise Java Bean. The actions are based on the JBoss AS, however any other EJB (3.0)-compliant container could be used instead.
| Note Before you begin working through this article, please ensure you have read the article Plain Old Java Object and have a working knowledge in the following areas:
|
Actions
This action section consists of three parts:
- Creating a container-agnostic Enterprise Java Bean
- Updating the container configuration
- Creating a unit test.
Creating a container-agnostic Enterprise Java Bean
To create a container-agnostic EJB which represents an instance of Erudine Behaviour Engine:
- In order to wrap EBE in an EJB, the first step is to create a Plain Old Java Object (POJO) that holds an instance of the engine. The interface of this object could be defined as follows:
package com.erudine.ebe; import com.erudine.utilities.exceptions.EngineException; public interface Executor { /** * The business interface that can be used by applications to run situations through a knowledgemodel. * This runs the situations through the default knowledgemodel, that is packaged with the application. * * @param situation is the Conceptual Graph that needs to be executed through the Behaviour * Execution Engine, must not be <code>null</code>. * @return a Map keyed on the node names where the situation has ended up, and the values * are the Conceptual Graphs that end up in that node, in string form. * @since 1.00 */ public ExecutorResponse execute(String situation) throws EngineException; /** * The business interface that can be used by applications to run situations through a given knowledgemodel * when a bean is configured with multiple knowledgemodels. * * @param situation is the Conceptual Graph that needs to be executed through the Behaviour * Execution Engine, must not be <code>null</code>. * @param modelName is the name of the KnowledgeModel that should be used for this execution * @return a Map keyed on the node names where the situation has ended up, and the values * are the Conceptual Graphs that end up in that node, in string form. * @since 1.00 */ public ExecutorResponse execute(String modelName, String situation) throws EngineException; }
An implementation of the Executor interface as well as the definition of the ExecutorResponse can be found in the Plain Old Java Object article. The classes included in the referenced article are a recommended implementation which can be used as a starting point for an implementation that fits your specific requirements and needs. - With the help of the Executor interface and its implementation SimpleExecutorImpl, you can now define an EJB that wraps EBE:
package com.erudine.ebe.ejb.agnostic.impl; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.annotation.security.PermitAll; import javax.ejb.EJBException; import com.erudine.ebe.Executor; import com.erudine.ebe.ExecutorResponse; import com.erudine.ebe.SimpleExecutorImpl; import com.erudine.ebe.ejb.ExecutorBeanLocal; import com.erudine.ebe.ejb.ExecutorBeanRemote; /** * Stateless Session Bean that wraps an instance of Behaviour Execution Engine * that allows you to process data through a knowledge model. * <p> * It exposes a remote interface which applications can invoke via RMI. It also exposes * a local interface which can be used by components within the same application server * to invoke operations. This is a generic bean that can be re-used with custom knowledgemodels. * It accepts two configurable parameters in the XML descriptor. * * ModelName - a simple string containing the name of the knowledgemodel that should * be used to instantiate the Behaviour Execution Engine. * StartNode - a simple string containing the name of the start node for the knowledgemodel. * * The parameters can be configured in ejb-jar.xml descriptor. * * @author Erudine * @version 1.00 * @since 1.00 */ public class SimpleExecutorBean implements ExecutorBeanRemote, ExecutorBeanLocal { Executor executor; @Resource(name="ModelName") String modelName; @Resource(name="StartNode") String startNode; @PostConstruct public void initEngine() { try { executor = new SimpleExecutorImpl( modelName, startNode ); } catch ( IOException e ) { throw new EJBException(e); } } @PermitAll public ExecutorResponse execute( String situation ) { return execute(null, situation); } @PermitAll public ExecutorResponse execute( String modelName, String situation ) { try { if(modelName != null) return executor.execute( modelName, situation ); else return executor.execute(situation); } catch (Exception e) { throw new EJBException(e); } } }
Erudine recommends that the knowledge model name and the name of the start node are not hard-coded within the EJB but defined within your AS configuration as shown in the example code. The interfaces ExecutorBeanLocal and ExecutorBeanRemote extend our Executor interface and are required in order to make the EJB accessible from inside and outside the AS:package com.erudine.ebe.ejb; import javax.ejb.Local; import com.erudine.ebe.Executor; @Local public interface ExecutorBeanLocal extends Executor { }
package com.erudine.ebe.ejb; import javax.ejb.Remote; import com.erudine.ebe.Executor; @Remote public interface ExecutorBeanRemote extends Executor { }
- The EJB, as well as its dependencies in the form of the interfaces, POJOS and Java libraries shipped with EBE, now needs to be packaged for deployment within the AS. This can be done in form of a simple Java Archive (JAR). The package also needs to include the authored knowledge model (.KNM file) and the erudine.config in the root of the archive and the configuration files described in the next section.
| Note The required Java libraries include all JAR files located in ERUDINE_HOME\Lib\dependencies and the ERUDINE_HOME\ErudineBehaviourEngine.jar. |
Updating the container configuration
To update the container configuration in order to integrate the EBE-EJB:
- The deployment descriptor for JBoss needs to be located in two XML files within your deployable JAR file:
- META-INF/ejb-jar.xml
- META-INF/jboss.xml
- The configuration of the EBE-EJB itself includes the name of the knowledge model and the name of the start node. In the case of the Schengen demo, the knowledge model is called SchengenBorderDemo and the start node is called Entrants. To configure the EBE-EJB, add the following snippet to your existing ejb-jar.xml file:
... <enterprise-beans> ... <session> <description> This EJB wraps the EBE allowing applications to call it remotely via RMI or locally within the same application server </description> <display-name>simple-ebe-ejb-agnostic</display-name> <ejb-name>SimpleExecutorBeanAgnostic</ejb-name> <ejb-class> com.erudine.ebe.ejb.agnostic.impl.SimpleExecutorBean </ejb-class> <session-type>Stateless</session-type> <env-entry> <env-entry-name>ModelName</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>SchengenBorderControl</env-entry-value> </env-entry> <env-entry> <env-entry-name>StartNode</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>Entrants</env-entry-value> </env-entry> </session> ... </enterprise-beans> ...
- If you want to access the EBE-EJB from outside the application server, define a JNDI (Java Naming and Directory Interface) name for the EJB as well. This is done in the jboss.xml file using the following snippet:
... <session> <ejb-name>SimpleExecutorBeanAgnostic</ejb-name> <jndi-name>ejb/remote/SimpleExecutorBeanAgnostic</jndi-name> </session> ...
- The EJB can also be configured using EJB 3.0-compliant annotations instead of the XML-based deployment descriptor described here. This is described in the Webservices article Enterprise Java Bean - Container Agnostic with Webservice Support.
Creating a unit test
To create a unit test which invokes EBE-EJB:
- 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 creates a Java naming context and lookup for the SchengenExecutor bean. It is assumed that the default port 1099 is used for the naming service.
... @BeforeClass public static void setUpInitialContextAndGetBean() throws NamingException { Hashtable<String, String> env = new Hashtable<String, String>(); env.put( Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory" ); env.put( Context.PROVIDER_URL, "jnp://localhost:1099" ); env.put( Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces" ); ctx = new InitialContext( env ); executor = (Executor) ctx.lookup( "ejb/remote/SimpleExecutorBeanAgnostic" ); } ... - The test case itself creates a simple conceptual graph in form of a String which is then used as the input for the executor. It tests if the conclusions result in the output node Allow Entry.
... @Test public void testExecuteSucess() { String situation = getTestSituation(); String expectedResult = "Allow Entry"; ExecutorResponse result = executor.execute( situation ); String resultNode = (String)result.getNodeList()[0].getNodeName(); assertEquals( expectedResult, resultNode ); } private String getTestSituation() { return "[Entrant: *Entrant]->(has)->[Surname: MUNSON*Surname]" + "[?Entrant]->(has)->[Forename: DEBORAH*Forename]" + "[?Entrant]->(has)->[Passport: 222952928*Passport]" + "[?Entrant]->(has)->[Gender: Female*Gender]" + "[?Entrant]->(has)->[Date of Birth: '10-1-1940'*Date of Birth]" + "[?Entrant]->(has)->[Visa: Long Stay Visa*Visa]" + "[?Entrant]->(has)->[Cash Carried: 3000*Cash Carried]"; } ...
Platform: all
EBE Version: 2.4
Category: Architecture and Deployment Guide
Author: Patrick Peisker