Writing an External Processor

In this tutorial we build an external processor that only allows events through that are in the southern hemisphere. Then we refine it so that the user can specify the latitude below which events are acceptable. All of the code is in the externalExample directory in the SOD distribution and the recipe files are in recipes.

Writing the processor class

The first step is to create a class that SOD will invoke to check for the proper latitude. Each subsetting or processing step in SOD has a corresponding Java interface that defines its operation. Check the external types document to see which step is appropriate for the operation you wish to perform. Since we want to check on the properties of an earthquake(origin in DHI terms) our class should implement the interface OriginSubsetter. The only requirement for an Origin subsetter is that it implement one method:

public StringTree accept(EventAccessOperations eventAccess, 
                         EventAttr eventAttr, 
                         Origin preferred_origin) throws Exception;

An implementation that checks for origins in the southern hemisphere is given below.

package edu.sc.seis.sod.example;

import edu.iris.Fissures.IfEvent.EventAttr;
import edu.iris.Fissures.IfEvent.Origin;
import edu.sc.seis.fissuresUtil.cache.CacheEvent;
import edu.sc.seis.sod.status.Fail;
import edu.sc.seis.sod.status.Pass;
import edu.sc.seis.sod.status.StringTree;
import edu.sc.seis.sod.subsetter.origin.OriginSubsetter;

public class SouthOMatic implements OriginSubsetter {

    public StringTree accept(CacheEvent eventAccess,
                             EventAttr eventAttr,
                             Origin preferred_origin) {
        if(preferred_origin.getLocation().latitude > 0) {
            return new Fail(this, "origin not in the southern hemisphere");
        }
        return new Pass(this);
    }
}				
If this SouthOMatic is listed in your recipe, SOD instantiates an instance of it and calls accept on it for every origin that passes through with the arguments eventAccess, eventAttr and preferred_origin describing the origin. Depending on the returned StringTree, SOD does further processing of the origin or stops at this point. StringTree is an abstract class that encapsulates a SOD processing result. For a simple processing steps like ours, we can use the two simplest subclasses of StringTree, Pass and Fail. Pass and Fail are both created with the subsetter as the first argument so SOD can log what subsetter performed the action. If an optional String reason is given this is also logged. As you can see we check if the event's preferredOrigin has a latitude greater than 0. If so, we return an instance of Fail and SOD will stop processing the origin. Otherwise we return Pass and things continue from this point.

Adding your external processor to a recipe

To get SOD to instantiate and use your new external processor you must add it to a recipe file. This is done with an externalOriginSubsetter ingredient for our reciple like so:

        <externalOriginSubsetter>
            <classname>edu.sc.seis.sod.example.SouthOMatic</classname>
        </externalOriginSubsetter>

The name for any external processor ingredient is always external followed by the name of the interface you've implemented. Then the first element inside the external element is a classname element with the fully qualified classname of the processor you've written. SOD uses reflection to find your class and instantiate it.

Compiling your processor

The final step to get the external processor to run is to compile the processor and add it to SOD's runtime classpath. To simplify this, we've included a Maven 1 project that handles getting all of the dependencies together and compiling any external processors located in externalExample/src. So the first step is to download and install Maven 1. Once you've got it setup, you can test the installation by going into externalExample and running maven jar. This should produce lots of messages about downloading jars and compiling classes and will eventually make a jar file and place it at externalExample/target/sodExternals-3.2.8.jar . This jar is already included in the classpath of the SOD scripts so you should be able to run sod -f externalExample/recipes/southOMatic.xml and see the origins south of the equator scroll by.

Customizing the processor from the recipe

In addition to the accept method, you may also add a constructor that takes a DOM Element in any external processor. If SOD sees this on an external, it'll pass in an org.w3c.dom.Element representing the external element from the recipe. This allows you to extract any extra configuration information out of the recipe that you need. With this we can make the maximum latitude configurable instead of always 0. Here is a variation of the SouthOMatic, LatOMatic, that does this. SOD includes a helper class, DOMHelper, that makes it easy to pull values out of the XML Element. Here we us the extractFloat method. Notice that we give a default value in case the recipe file does not contain a maxLat element. The default value is optional, but if it is not given and the recipe does not have that value then an exception will be thrown.

package edu.sc.seis.sod.example;

import org.w3c.dom.Element;

import edu.iris.Fissures.IfEvent.EventAttr;
import edu.iris.Fissures.IfEvent.Origin;
import edu.sc.seis.fissuresUtil.cache.CacheEvent;
import edu.sc.seis.fissuresUtil.display.configuration.DOMHelper;
import edu.sc.seis.sod.status.Fail;
import edu.sc.seis.sod.status.Pass;
import edu.sc.seis.sod.status.StringTree;
import edu.sc.seis.sod.subsetter.origin.OriginSubsetter;

public class LatOMatic implements OriginSubsetter {

    public LatOMatic(Element el) {
        maxLat = DOMHelper.extractFloat(el, "maxLat", 0);
    }

    public StringTree accept(CacheEvent eventAccess,
                             EventAttr eventAttr,
                             Origin preferred_origin) throws Exception {
        if(preferred_origin.getLocation().latitude > maxLat) {
            return new Fail(this, "origin not below " + maxLat + " latitude");
        }
        return new Pass(this);
    }

    float maxLat;
}				
        <externalOriginSubsetter>
            <classname>edu.sc.seis.sod.example.LatOMatic</classname>
            <maxLat>45</maxLat>
        </externalOriginSubsetter>

The only changes needed from southOMatic.xml are the new classanme and the addition of the maxLat element. If you run sod -f externalExamples/recipes/latOMatic.xml you can see that the events printed out are now at latitude 45 or below.