Writing a Processor

This section of the guide covers aspects of SPI.

There are two types of Processors in Synapse:

  1. Node Processors

    These can contain sub-processors in its processor map. Ex: RegexProcessor, XpathProcessor etc.

  2. Leaf Processors

    This does not contain any sub-processors. Their soul purpose is to contain configuration infromation. Ex: AddressinInProcessor Core of Synapse contain Group and Referencing Processors, Rule Processors, Built-in Processors and User Mediator Type Processors.

Apart from the above, Synapse considers all other processors as extensions. Ex: SpringMediator etc. As mentioned in the Userguide every element in synapse.xml maps to a Processor. Every Processor has its ProcessorConfigurator. ProcessorConfigurator is the one which decides whether the corresponding Processor is a Node or Leaf Processor. So any Processor that has been written to Synapse should come as a extension and should go under the SVN folder "extensions".

Writing a Node Processor

Following XML shows the semantics of a Node Processor.

<foo attr="value">
     <bar/>
     <car/>
</foo>
 

<foo/> should map to FooProcessor which is a Node and there should be corresponding FooProcessorConfiguratator. So FooProcessor should contain a map to hold down the other Processors coming under it (BarProcessor and CarProcessor, these will be explained later on). To be a Node, FooProcessorConfigurator should extend from abstract AbstractListProcessorConfigurator. FooProcessorConfigurator contains all the configuration information of the Processor. It will hold the QName of element. All elements comes under the namespace of http://ws.apache.org/ns/synapse. If there are other attributes pertaining to the elements they should also come under the prior namespace and it should be defined in the ProcesssorConfigurator. Ultimately you will have following code.


public class FooProcessorConfigurator extends AbstractListProcessorConfigurator {

       private static final String FOO = "foo";
       private static final QName FOO_Q = new QN'ame (http://ws.apache.org/ns/synapse , FOO);
       private static final QName ATTR = new QName("attr");
        
       ...

}

Now you have to implement the following methods,

public Processor createProcessor(SynapseEnvironment se, OMElement el);public QName getTagQName();

public Processor createProcessor(SynapseEnvironment se, OMElement el),

This will deal with the FooProcessor creation. The following is a specific way to write this method


public class FooProcessorConfigurator extends AbstractListProcessorConfigurator {
       ...
       public Processor createProcessor(SynapseEnvironment se, OMElement el) {
                FooProcessor fooProcessor = new FooProcessor();
                super.addChildrenAndSetName(se, el, fooProcessor);

                OMAttribute attr= el.getAttribute(ATTR);
                if (patt == null) {
                        throw new SynapseException(FOO + " must have "
                                        + ATTR+ " attribute: " + el.toString());
                }

                fooProcessor.setAttr(attr.getAttributeValue());
                return fooProcessor;
        }

        public QName getTagQName() {
                return FOO_Q;
        }

} 

Now lets look at the FooProcessor implementation. Remember it's a Node, and it should contain a place to hold the value of "attr". As this is a Node, it should extend from ListProcessor.


public class RegexProcessor extends ListProcessor {
        
        private Log log = LogFactory.getLog(getClass());

        private String attr  = null;

        public void setAttr(String attr) {
                this.attr = attr;
        }

        public String getAttr() {
                return this.attr;
        }

        
        public boolean process(SynapseEnvironment se, SynapseMessage smc) {
                       // Processing logic goes here
                // there be any processing condition, at the you should call
                return super.process(se, smc);
                // or
                return true;
        } 

}
public boolean process(SynapseEnvironment se, SynapseMessage smc) 

handle the processing logic. So there be any condition, and if the logic is "true" call "super.process(se, smc)" . If the processing logic is fault through make sure that you will call "return true". So writing a extension is as easy as prior.

Writing a Leaf Processor

<bar/> is leaf element and it will map to a Leaf Node. So let the mapping be BarProcessor and there should be the corresponding BarProcessorConfigurator. So the symantics of BarProcessorConfigurator is as follows,


public class BarProcessorConfigurator extends AbstractProcessorConfigurator {
        private static final QName BAR_Q = new QName(http://ws.apache.org/ns/synapse,"bar");


        public QName getTagQName() {
                return BAR_Q;
        }


        public Processor createProcessor(SynapseEnvironment se, OMElement el) {
                BarProcessor barProcessor = new BarProcessor();
                super.setNameOnProcessor(se,el,barProcessor);
                return barProcessor;
        }

} 

If there are attributes, let them be handle as shown in "Writing a Node Processor" . Leaf ProcessorConfigurators should extend from AbstractProcessorConfigurator. Now lets see the semantics of BarProcessor.


public class BarProcessor extends AbstractProcessor {
        
        private Log log = LogFactory.getLog(getClass());

        public boolean process(SynapseEnvironment se, SynapseMessage smc) {
                // Processing logic goes here
                return true;
        }
} 

Leaf Processors should extend from AbstractProcessor. So "Let there be Processors". But we are not quite there. We need to do one more configuration, Plunging the extension to the core.

First Method:

This is based on Service Provider funtionality that comes with JDK1.3 above. For more information click here

  1. Create a META-INF folder somewhere in your System
  2. Create services folder inside {META-INF/services}
  3. Create a text file with the QName of interface a.b.c.d.ProcessorConfigurator
  4. So you will have a text file in the this structure as META-INF/services/a.b.c.d.ProcessorConfigurator
  5. Inside the text file write the entry of the QName of implementation class of the interface a.b.c.d.BarProcessorConfigurator
  6. Now create a Jar file using the structure. Make sure the class a.b.c.d.BarProcessorConfigurator inside the Jar or in the classpaht and make sure that Jar is in your classpath when running the Synapse server

    JAR structure is as follows

        META-INF
               +-services
                        +-a.b.c.d.ProcessorConfigurator {this is a text file, which has a QName entry a.b.c.d.BarProcessorConfigurator }
        a
         +-b
           +-c
             +-d
               +-BarProcessorConfigurator
    

Second Method

Go to the class

org.apache.synapse.xml.ProcessorConfiguratorFinder  

there one will find the following static variable.

private static Class[] processorConfigurators = {...}

You have to fill it with your extensions as follows,

private static Class[] processorConfigurators = {..., FooProcessorConfigurator.class,BarProcessorConfigurator.class} 

Now you have successfully plugged your processor into Synapse. Finally "Let there be Processors".