Servlet

Purpose

To create a container-agnostic Java Servlet which wraps an instance of Erudine Behaviour Engine (EBE).

Theory

Erudine Behaviour Engine can be deployed in various ways. One deployment scenario is using a Java Servlet 2.5-compliant Web Container or Application Server (AS). In order to deploy EBE to such an environment, it needs to be wrapped in a Servlet. The following actions will guide you through the implementation and deployment of a Java Servlet 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 Java Servlet. The actions are based on the JBoss AS, however any other Java Servlet (2.5)-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:
  • Java Platform, Enterprise Edition 5 (Java EE 5)
  • Java Application Server
  • Java Unit Testing

Actions

This action section consists of three parts:

Creating a container-agnostic Java Servlet

To create a container-agnostic Java Servlet which represents an instance of Erudine Behaviour Engine:

  1. In order to wrap EBE in a Servlet, 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.
  2. With the help of the Executor interface and its implementation SimpleExecutorImpl, you can now define a Servlet that wraps EBE:
    package com.erudine.ebe.web.servlet;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.erudine.ebe.Executor;
    import com.erudine.ebe.ExecutorResponse;
    import com.erudine.ebe.SimpleExecutorImpl;
    import com.erudine.utilities.exceptions.EngineException;
    
    /**
     * The main servlet class that hosts an instance of the Behaviour Engine. It accepts
     * requests over HTTP post, runs them through the knowledgemodel and returns the
     * result as part of the HTTP response. The model to use and the start node to use,
     * can be configured in the web.xml using the init parameters ModelName and StartNode
     * respectively.
     * 
     * @author Erudine
     * @since 1.00
     */
    public class ExecutorServlet extends HttpServlet {
    
        private static final long serialVersionUID = -333704954056355028L;
        
        private static final String INIT_PARAMETER_KNM_NAME = "ModelName";
        private static final String INIT_PARAMETER_START_NODE = "StartNode";
    
        Executor executor;
    
        public void init() throws ServletException {
            super.init();
    	String model = getInitParameter(INIT_PARAMETER_KNM_NAME);
    	String startNode = getInitParameter(INIT_PARAMETER_START_NODE);
    	if (model == null || startNode == null) {
    	    throw new ServletException("Missing init parameter");
    	}
            try {
    	    executor = new SimpleExecutorImpl(model, startNode);
            } catch ( Exception e ) {
                throw new ServletException(e);
            }
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            try {
    	    String situation = extractSituation(request.getInputStream());
    	    ExecutorResponse result = executor.execute(situation);
    	    respond(response, "text/plain", result.toString());
    	} catch (Exception e) {
    	    throw new EngineException(e);
    	}
        }
    
        private String extractSituation(InputStream inputStream) throws IOException {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    	StringBuilder stringBuilder = new StringBuilder();
    	String line = null;
    	while ((line = bufferedReader.readLine()) != null) {
    	    stringBuilder.append(line + "\n");
    	}
    	return stringBuilder.toString();    
        }
    
        private void respond(HttpServletResponse response, String mimeType, String responseString) {
            response.setContentType(mimeType);
    	try {
    	    PrintWriter writer = response.getWriter();
    	    writer.write(responseString);
    	    writer.flush();
    	    writer.close();
    	} catch (IOException e) {
    	    throw new RuntimeException(e);
    	}
        }
    }
    

    Erudine recommends that the knowledge model name and the name of the start node are not hard-coded within the Servlet but defined within your WS/AS configuration, as shown in the example code (getInitParameter()).
  3. The Java Servlet, 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 WS/AS. This can be done in the form of a simple Web Application Archive (WAR). The package also needs to include the authored Knowledge Model (.KNM file) and the erudine.config in the WEB-INF\classes\ directory of the archive and the web.xml configuration file 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-Servlet:

  1. In this deployment scenario, the deployment descriptor consists of the web.xml file located in the WEB-INF directory of your deployable WAR file.
  2. The configuration of the EBE-Servlet 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 SchengenBorderControl and the start node is called Entrants. To configure the EBE-Servlet, add the following snippet to your existing web.xml file:
    ...
        <servlet>
            <servlet-name>ExecutorServlet</servlet-name>
            <display-name>ExecutorServlet</display-name>
    	<servlet-class>
    	    com.erudine.ebe.web.servlet.ExecutorServlet
    	</servlet-class>
    	<init-param>
    	    <param-name>ModelName</param-name>
    	    <param-value>SchengenBorderControl</param-value>
    	    <description>Name of the Knowledgemodel to use</description>
    	</init-param>
    	<init-param>
    	    <param-name>StartNode</param-name>
    	    <param-value>Entrants</param-value>
    	    <description>Node to start processing situations from</description>
    	</init-param>
    	<load-on-startup>1</load-on-startup>
        </servlet>
    	
        <servlet-mapping>
            <servlet-name>ExecutorServlet</servlet-name>
            <url-pattern>/SchengenExecutor/*</url-pattern>
        </servlet-mapping>
    ...
    

Creating a unit test

To create a unit test which invokes the EBE-Servlet:

  1. After the successful deployment of the EBE-Servlet WAR 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 an HTTP POST method containing a test situation and an HTTPClient in order to post the request.
    ...
        @BeforeClass
        public static void setUp() throws Exception {
            post = new PostMethod(URL);
    	RequestEntity entity = new StringRequestEntity(getTestSituation());
    	post.setRequestEntity(entity);
    	httpClient = new HttpClient();
        }
    ...
    
  2. The test case itself uses the created HTTPClient to execute the HTTP POST method. The utility method getEndNode extracts the knowledge node name of the retrieved response. In this particular case it is testing that the conclusions of EBE result in the output node Allow Entry.
    ...
        @Test
        public void testExecuteSucess() {
            try {
    	    httpClient.executeMethod(post);
                String response = post.getResponseBodyAsString();
                assertEquals("Allow Entry", getEndNode(response));
            } catch (Exception e) {
                e.printStackTrace();
                Assert.fail("Error returned by server " + e);
            }
        }
    
        private static 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 getEndNode(String result) {
            ExecutorResponse response = ExecutorUtilities.stringToResponse(result);
            return response.getNodeList()[0].getNodeName();
        }
    ...
    

Platform: all
EBE Version: 2.4
Category: Architecture and Deployment Guide
Author: Patrick Peisker


Browse Space

- Pages
- News
- Labels
- Attachments
- Bookmarks
- Mail
- Activity
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

 

Other Features

Add Content