<--development ^--Soigan--^ even more development-->

Soigan - a Multicast XML monitoring system - development, continued

Development, continued

Now that we have a working plugin, let's write a Worker that can communicate to it.

Worker

A Worker can be broken up into four main parts:

Configuration file reader

Here's our complete soigan.conf that we want to parse.
<configuration>

<network>
  <worker>
    <port>5106</port>
  </worker>
</network>

<permissions>
  <worker>
    <allow>127.0.0.1</allow>
  </worker>
</permissions>

<plugins>
  <plugin>
    <name>users</name>
    <params>
    </params>
    <command>java soigan_who</command>
  </plugin>
</plugins>

<workers/>

</configuration>
We chose XML because there are libraries in all languages for it, but this file is also pretty simple, and should be parseable by those that don't want to use XML directly. That being said, I'm almost embarrassed to say that my first implementation of the configuration reader is written in a pretty bad way. While I did use the Apache Xerces library, I'm sure I didn't use it to its potential.

To read the configuration file, I created a ConfReader that reads each section in, and then provides a bunch of functions that a Worker or Server can call to get themselves configured correctly. Let's take a look at the complete sourcecode for soigan_worker.java:

import org.apache.xmlrpc.WebServer;

class soigan_worker {
  public static void main(String args[]) {
    ConfReader cr;
    try {
      cr=new ConfReader(args[0]);
    } catch (Exception e) {
      cr=new ConfReader("soigan.conf");
    }

    if (cr.isValid()) {
      WebServer server=new WebServer(cr.getWorkerPort());
      cr.setWorkerPerms(server);

      WorkerHandler handler=new WorkerHandler();
      cr.setWorkerPlugins(handler);

      server.addHandler("plugin",handler.getPluginHandler());
      server.addHandler("schema",handler.getSchemaHandler());

      server.start();
  } else {
    System.err.println("Invalid configuration file.");
  }
}
A getWorkerPort() is used to set the right port for the Worker to listen to. setWorkerPerms() configures the server to accept or deny from specified ports. The WorkerHandler (discussed shortly) is configured through setWorkerPlugins(). This sets up all of the plugins that the Worker should respond to.

XML-RPC server

As seen above, the XML-RPC server is pretty easy to instantiate. org.apache.xmlrpc.WebServer is the class that sets up a listener, restricts access to and from certain addresses, and has handlers that listen to the different function calls. The WorkerHandler class bundles the callback functions for each of plugin and schema and keeps track of all the known plugins from the configuration file. Here's a look at an early version of plugin.run:
    public boolean run(final String host, Date date, String name, final Vector options) {
      boolean returncode=false;
      if (plugins!=null) {
        Object o=plugins.get(name);
        if (o!=null) {
          final Plugin p=(Plugin)o;
          if (p.checkParams(options)) {
            returncode=true;
            (new Thread() {
                public void run() {
                  p.run(host,options);
                }
              }).start();
          }
        }
      }
      return returncode;
    }
  }  
This function is called whenever the WebServer above receives a call to plugin.run() from a Server. There's no error-checking or error-handling here, but basically the list of registered plugins is checked, and if a match is found, the plugin is run. Note that this is done in a separate thread so the plugin.run() function can return right away to the Server to let it know that the Request was successful. This continues to our next step.

plugin handler

A Plugin object is instantiated for each entry in the configuration file. It holds the command to execute and the options to that command. It is responsible for calling the plugin, and for converting the result to an XML-RPC call. With all that done, it does the other half of its work.

XML-RPC client

The Plugin class is also responsible for calling back the Server with the results of the plugin. A getParams() function (with two helpers, getStruct() and getArray()) processes the returned XML-RPC-ready output from the plugin, turning it into the Vector that we've set up as our return type. You can see above in the code
                  p.run(host,options);
That we pass all the Plugin class needs to send back the result, which is the host where the Server is running.

Testing

It's possible to test our Worker without fully implementing a Server. I did this by writing a Proxy class that will show me all the traffic going back and forth. I then used the following WorkerTest program:
import org.apache.xmlrpc.XmlRpcClient;
import java.util.Vector;

class WorkerTest {
  public static void main(String args[]) {
    Object result;
    try {
      XmlRpcClient client=new XmlRpcClient("http://127.0.0.1:8181/");
      
      Vector params=new Vector();
      
      params.addElement(new String("localhost:8282"));
      params.addElement(java.util.Calendar.getInstance().getTime());
      params.addElement(new String("who"));
      params.addElement(new Vector());
      
      result=client.execute("plugin.run",params);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
I ran two Proxy programs, one that listened on port 8181 and passed the data between that and port 5016 (our Worker port), and another on port 8282 directing the traffic to a webserver (so we have something for the Proxy to pass the Result to). The output doesn't display nicely on a webpage, but functional XML-RPC was going back and forth.

And that's about all there is to our Worker. Now the other half.
<--development ^--Soigan--^ even more development-->

©2002-2017 Wayne Pearson