Chapter 8. External Processes

One prominent application for agents is wrapping legacy systems and "agentifying" them. Hence, it is an important point how separate processes can interact with Jadex agents as these applications often use other means of communications such as sockets or RMI. A Jadex agent executes behaviour sequentially and does not allow any parallel access to its internal structures due to integrity constraints. For this reason it is disallowed and discouraged to block the active plan thread e.g. by opening sockets and waiting for connections or simply by calling Thread.sleep(). This can cause the whole agent to hang because the agent waits for the completion of the current plan step. It will possibly abort the plan when the maximum plan step execution time has been exceeded (if the maximum execution is restricted within the agent runtime.properties). When external processes need to interact directly with the agent, they have to use methods from the so called jadex.runtime.IExternalAccess, which offers the most common agents methods.

8.1. Exercise G1 - Socket Communication

We extend the simple translation agent from exercise C2 with a plan that sets up a server socket which listens for translation requests. Whenever a new request is issued (e.g. from a browser) a new goal containing the client connection is created and dispatched. The translation plan handles this translation goal and sends back some HTML content including some text and the translated word.

Create a new file for the ServerPlanG1. 

  • Declare the ServerSocket as attribute within the plan

    protected ServerSocket server;

  • Create a constructor which takes the server port as argument and create a the server within it:

    this.server	= new ServerSocket(port);
    getLogger().info("Created: "+server);

  • In the body simply start a new thread that will handle client request in the run method. Additionally do not quit the plan to be able to shut down the server when the agent is killed:

    new Thread(this).start();
    waitFor(IFilter.NEVER);

  • When the agent dies its plans are aborted. Hence, in the aborted method the server can be closed:

    public void aborted(){
        server.close();
    }

  • In the threads run method create and dispatch goals for every incoming request:

    while(true)
    {
        Socket    client    = server.accept();
        IGoal goal = getExternalAccess().getGoalbase().createGoal("translate");
        goal.getParameter("client").setValue(client);
        getExternalAccess().getGoalbase().dispatchTopLevelGoal(goal);
    }

Modify the EnglishGermanTranslationPlanG1 to handle translation goals. 

  • Extract the socket from the goal and read the English word:

    Socket client = (Socket)getRootGoal().getParameter("client").getValue();
    BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
    String request = in.readLine();
    // Read the word to translate from the input string

  • Translate the word as usual by using the query

  • Send back answer to the client:

    PrintStream out = new PrintStream(client.getOutputStream());
    out.print("HTTP/1.0 200 OK\r\n");
    out.print("Content-type: text/html\r\n"); out.println("\r\n");
    out.println("<html><head><title>TranslationG1 - "+eword
        +"</title></head><body>");
    out.println("<p>Translated from english to german: "+eword+" = "+gword+".");
    out.println("</p></body></html>");
    client.close();

Create a file TranslationG1.agent.xml by copying TranslationC2.agent.xml. 

  • The addword plan and event declarations are not used and can be removed for clarity.

  • Introduce the translation goal type:

    <achievegoal name="translate">
        <parameter name="client" class="java.net.Socket"/>
    </achievegoal>

  • Introduce the new plan for setting up the server and start the plan initially:

    <plan name="server">
        <body>new ServerPlanG1(9099)</body>
    </plan>
    ...
    <initialstates>
        <initialstate name="default">
            <plans>
                <initialplan ref="server"/>
            </plans>
        </initialstate>
    </initialstates>

  • Modify the trigger of the translation plan to react on translation goals

    <plan name="egtrans">
        <body>new EnglishGermanTranslationPlanG1()</body>
        <trigger>
            <goal ref="translate"/>
        </trigger>
    </plan>

Start and test the agent. 

  • Start the agent and open a browser to issue translation request. This can be done by entering the server url and appending the word to translate, e.g. http://localhost:9099/dog. The result should be printed out in the returned web page.