| <html>
|
| <head>
|
| <meta content="text/html; charset=ISO-8859-1"
|
| http-equiv="content-type">
|
| <title>Extending Synapse</title>
|
| </head>
|
| <body>
|
| <h1>Extending Synapse<br>
|
| </h1>
|
| <br>
|
| <h2>Writing custom Mediator implementations</h2>
|
| <p>The primary interface of the Synapse API is the MessageContext
|
| interface
|
| defined below. This essentially defines the per-message context passed
|
| through the chain of mediators, for each and every message received and
|
| processed by
|
| Synapse. Each message instance is wrapped within a MessageContext
|
| instance, and the message context is set with the references to the
|
| SynapseConfiguration and SynapseEnvironments to be used. The
|
| SynapseConfiguration holds the global configuration model that defines
|
| message
|
| mediation rules and common definitions to be used, while the
|
| environment gives
|
| access to the underlying SOAP implementation used.
|
| A typical mediator would need to manipulate the MessageContext by
|
| referring to the SynapseConfiguration. However it is strongly
|
| recommended that
|
| the SynapseConfiguration should not be updated by mediator instances as
|
| it is shared by all messages, and may be updated by Synapse
|
| administration or configuration modules.
|
| Mediator instances may store custom named properties into the
|
| MessageContext
|
| for later retrieval by successive mediators.<br>
|
| </p>
|
| <h4>MessageContext Interface</h4>
|
| <p></p>
|
| <pre>
|
| package org.apache.synapse;
|
|
|
| import ...
|
|
|
| public interface MessageContext {
|
|
|
| /**
|
| * Get a reference to the current SynapseConfiguration
|
| *
|
| * @return the current synapse configuration
|
| */
|
| public SynapseConfiguration getConfiguration();
|
|
|
| /**
|
| * Set or replace the Synapse Configuration instance to be used. May be used to
|
| * programatically change the configuration at runtime etc.
|
| *
|
| * @param cfg The new synapse configuration instance
|
| */
|
| public void setConfiguration(SynapseConfiguration cfg);
|
|
|
| /**
|
| * Returns a reference to the host Synapse Environment
|
| * @return the Synapse Environment
|
| */
|
| public SynapseEnvironment getEnvironment();
|
|
|
| /**
|
| * Sets the SynapseEnvironment reference to this context
|
| * @param se the reference to the Synapse Environment
|
| */
|
| public void setEnvironment(SynapseEnvironment se);
|
|
|
| /**
|
| * Get the value of a custom (local) property set on the message instance
|
| * @param key key to look up property
|
| * @return value for the given key
|
| */
|
| public Object getProperty(String key);
|
|
|
| /**
|
| * Set a custom (local) property with the given name on the message instance
|
| * @param key key to be used
|
| * @param value value to be saved
|
| */
|
| public void setProperty(String key, Object value);
|
|
|
| /**
|
| * Returns the Set of keys over the properties on this message context
|
| * @return a Set of keys over message properties
|
| */
|
| public Set getPropertyKeySet();
|
|
|
| /**
|
| * Get the SOAP envelope of this message
|
| * @return the SOAP envelope of the message
|
| */
|
| public SOAPEnvelope getEnvelope();
|
|
|
| /**
|
| * Sets the given envelope as the current SOAPEnvelope for this message
|
| * @param envelope the envelope to be set
|
| * @throws org.apache.axis2.AxisFault on exception
|
| */
|
| public void setEnvelope(SOAPEnvelope envelope) throws AxisFault;
|
|
|
| /**
|
| * SOAP message related getters and setters
|
| */
|
| public ....get/set()...
|
|
|
| }
|
| </pre>
|
| <p>The MessageContext interface is based on the Axis2 <a>MessageContext</a>
|
| interface, and uses the Axis2 <a>EndpointReference</a> and
|
| SOAPEnvelope
|
| classes/interfaces.</p>
|
| <p>The purpose of this interface is to capture a message as it flows
|
| through the system. As you will see the messages are represented using
|
| the SOAP infoset. Binary messages can be embedded in the Envelope using
|
| the MTOM support built into Axis2's AXIOM object model.</p>
|
| <h4>Mediator interface</h4>
|
| <p>The second key interface for mediator writers is the Mediator
|
| interface:</p>
|
| <pre>package org.apache.synapse.api; |
| |
| import org.apache.synapse.MessageContext; |
| |
| /** |
| * All Synapse mediators must implement this Mediator interface. As a message passes |
| * through the synapse system, each mediator's mediate() method is invoked in the |
| * sequence/order defined in the SynapseConfiguration. |
| */ |
| public interface <span
|
| style="font-weight: bold;">Mediator </span>{ |
| |
| /** |
| * Invokes the mediator passing the current message for mediation. Each |
| * mediator performs its mediation action, and returns true if mediation |
| * should continue, or false if further mediation should be aborted. |
| * |
| * @param synCtx the current message for mediation |
| * @return true if further mediation should continue |
| */ |
| public boolean mediate(MessageContext synCtx); |
| |
| /** |
| * This is used for debugging purposes and exposes the type of the current |
| * mediator for logging and debugging purposes |
| * @return a String representation of the mediator type |
| */ |
| public String getType(); |
| }</pre>
|
| <p>A mediator can read and/or modify the <a>SynapseMessage</a> in any
|
| suitable
|
| manner - adjusting the routing headers or changing the message body. If
|
| the mediate() method
|
| returns false, it signals to the Synapse processing model to stop
|
| further processing of the message. For example, if the mediator is a
|
| security agent it
|
| may decide that this message is dangerous and should not be processed
|
| further. This is generally the exception as mediators are usually
|
| designed to co-operate to process the message onwards. |
|
|
| |
|
|
| </p>
|
| <h3>Leaf and Node Mediators, List mediators and Filter mediators |
|
|
| </h3>
|
| Mediators may be Node mediators (i.e. these contain sub mediators) or
|
| Leaf mediators (mediators that does not hold any sub mediators). A Node
|
| mediator must implement the org.apache.synapse.api.ListMediator
|
| interface listed below, or extend from the
|
| org.apache.synapse.mediators.AbstractListMediator. |
|
|
| <h4>The ListMediator interface |
|
|
| </h4>
|
| <pre>
|
| package org.apache.synapse.api;
|
|
|
| import java.util.List;
|
|
|
| /**
|
| * The List mediator executes a given sequence/list of child mediators
|
| */
|
| public interface ListMediator extends Mediator {
|
| /**
|
| * Appends the specified mediator to the end of this mediator's (children) list
|
| * @param m the mediator to be added
|
| * @return true (as per the general contract of the Collection.add method)
|
| */
|
| public boolean addChild(Mediator m);
|
|
|
| /**
|
| * Appends all of the mediators in the specified collection to the end of this mediator's (children)
|
| * list, in the order that they are returned by the specified collection's iterator
|
| * @param c the list of mediators to be added
|
| * @return true if this list changed as a result of the call
|
| */
|
| public boolean addAll(List c);
|
|
|
| /**
|
| * Returns the mediator at the specified position
|
| * @param pos index of mediator to return
|
| * @return the mediator at the specified position in this list
|
| */
|
| public Mediator getChild(int pos);
|
|
|
| /**
|
| * Removes the first occurrence in this list of the specified mediator
|
| * @param m mediator to be removed from this list, if present
|
| * @return true if this list contained the specified mediator
|
| */
|
| public boolean removeChild(Mediator m);
|
|
|
| /**
|
| * Removes the mediator at the specified position in this list
|
| * @param pos the index of the mediator to remove
|
| * @return the mediator previously at the specified position
|
| */
|
| public Mediator removeChild(int pos);
|
|
|
| /**
|
| * Return the list of mediators of this List mediator instance
|
| * @return the child/sub mediator list
|
| */
|
| public List getList();
|
| }
|
|
|
| </pre> |
|
|
| A ListMediator implementation should call super.mediate(synCtx) to
|
| process its sub mediator sequence. A FilterMediator is a ListMediator
|
| which executes its sequence of sub mediators on successful outcome of a
|
| test condition. Mediator instance which performs filtering should
|
| implement the FilterMediator interface. |
|
|
| |
|
|
| <h4>FilterMediator interface</h4>
|
| <pre>package org.apache.synapse.api; |
| |
| import org.apache.synapse.MessageContext; |
| |
| /** |
| * The filter mediator is a list mediator, which executes the given (sub) list of mediators |
| * if the specified condition is satisfied |
| * |
| * @see FilterMediator#test(org.apache.synapse.MessageContext) |
| */ |
| public interface <span
|
| style="font-weight: bold;">FilterMediator </span>extends ListMediator { |
| |
| /** |
| * Should return true if the sub/child mediators should execute. i.e. if the filter |
| * condition is satisfied |
| * @param synCtx |
| * @return true if the configured filter condition evaluates to true |
| */ |
| public boolean test(MessageContext synCtx); |
| } |
| </pre>
|
| <h2>Writing custom Configuration implementations for mediators |
|
|
| </h2>
|
| You may write your own custom configurator for the Mediator
|
| implementation you write without relying on the Class mediator or
|
| Spring
|
| extension for its initialization. You could thus write a
|
| MediatorFactory implementation which defines how to digest a custom
|
| XML configuration element to be used to create and configure the custom
|
| mediator instance. The custom
|
| MediatorFactory implementation and the mediator class must be bundled
|
| in a JAR file conforming to the J2SE Service Provider model (See the
|
| description for Extensions below for more details and examples) and
|
| placed into the SYNAPSE_HOME/lib folder, so that the Synapse runtime
|
| could find and load the definition.
|
| Essentially this means that a custom JAR file must bundle your class
|
| implementing the Mediator interface, and the MediatorFactory
|
| implementation class and contain a text file by the name
|
| "org.apache.synapse.config.xml.MediatorFactory" which will contain the
|
| fully qualified name(s) of your MediatorFactory implementation
|
| class(es). You should also place any dependency JARs into the same lib
|
| folder so that the correct classpath references could be made. |
|
|
| |
|
|
| The
|
| MediatorFactory interface listing is given below, which you should
|
| implement, and its getTagQName()
|
| method must define the fully qualified element of interest for custom
|
| configuration. The Synapse initialization will call back to this
|
| MediatorFactory
|
| instance through the
|
| createMediator(OMElement elem) method passing in this XML element, so
|
| that an instance of the mediator could be created utilizing the custom
|
| XML specification and returned.
|
| See the ValidateMediator and the ValidateMediatorFactory classes under
|
| modules/extensions in the Synapse source distribution for examples. |
|
|
| |
|
|
| <h4>The MediatorFactory interface</h4>
|
| <pre>package org.apache.synapse.config.xml; |
| |
| import ... |
| |
| /** |
| * A mediator factory capable of creating an instance of a mediator through a given |
| * XML should implement this interface |
| */ |
| public interface MediatorFactory { |
| /** |
| * Creates an instance of the mediator using the OMElement |
| * @param elem |
| * @return the created mediator |
| */ |
| public Mediator createMediator(OMElement elem); |
| |
| /** |
| * The QName of this mediator element in the XML config |
| * @return QName of the mediator element |
| */ |
| public QName getTagQName(); |
| } |
| </pre>
|
| <h2>Writing custom Configuration implementations for Configuration
|
| extensions</h2>
|
| Mediators could be configured in multiple ways as Synapse, i.e.
|
| Either through an XML configuration file, or programatically. If
|
| Synapse
|
| is initialized with a Synapse XML configuration which is the default,
|
| and you want to create a custom extension to the global Synapse
|
| configuration,
|
| you must implement the org.apache.synapse.config.Extension
|
| interface, and you must write your custom ExtensionFactory
|
| implementation which will create the instance of your extension, so
|
| that
|
| it could be set into the SynapseConfiguration as a named global
|
| 'property'.
|
| The interfaces referenced are listed below. |
|
|
| |
|
|
| <h4>The Extension interface</h4>
|
| <pre>package org.apache.synapse.config; |
| |
| import ... |
| |
| /** |
| * An Extension allows the Synapse configuration to be extended. The Spring |
| * configuration support is implemented as such an extension, so that the |
| * Synapse core will not be dependent on Spring classes. An extension |
| * <b>must</b> specify the following methods to set and get its name. |
| */ |
| public interface Extension { |
| |
| public String getName(); |
| |
| public void setName(String name); |
| |
| } |
| </pre>
|
| <h4>The ExtensionFactory interface |
|
|
| </h4>
|
| <pre>package org.apache.synapse.config.xml; |
| |
| import .... |
| |
| /** |
| * A extension factory that is capable of creating an instance of a |
| * named extension, through a given XML, should implement this interface |
| */ |
| public interface ExtensionFactory { |
| /** |
| * Creates an instance of a named extension using the OMElement |
| * @param elem |
| * @return the created named extension |
| */ |
| public Extension createExtension(OMElement elem); |
| |
| /** |
| * The QName of the extension element in the XML config |
| * @return QName of the extension element |
| */ |
| public QName getTagQName(); |
| |
| } |
| </pre>
|
| <h4>Loading of Extensions by the Synapse runtime</h4>
|
| Synapse loads available extensions from the runtime classpath using the
|
| <a
|
| href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">J2SE
|
| Service Provider model</a>.
|
| This essentially iterates over the available JAR files, for a
|
| META-INF/services directory within each file, and looks for a
|
| text
|
| file with the name org.apache.synapse.config.xml.ExtensionFactory which
|
| contains a list of fully qualified classname that implement the above
|
| interface, listing each class in a separate line. |
|
|
| |
|
|
| e.g. The built-in extension_mediators.jar contains the following
|
| structure |
|
|
| |
|
|
| <pre>extension_mediators.jar |
| /META-INF/services |
| org.apache.synapse.config.xml.ExtensionFactory |
| org.apache.synapse.config.xml.MediatorFactory |
| /... the implementation classes as usual... |
| |
| </pre>
|
| The org.apache.synapse.config.xml.ExtensionFactory text file discussed
|
| above contains the line
|
| "org.apache.synapse.config.xml.SpringConfigExtensionFactory" but may
|
| contain multiple lines specifying a list of extensions contained within
|
| the specific JAR file. |
|
|
| |
|
|
| Once the available ExtensionFactory implementations are loaded by the
|
| Synapse initializer, it accumulates the list of defined XML
|
| configuration elements by calling to the getTagQName() method of the
|
| ExtensionFactory implementation. Lets consider the Spring extension as
|
| an example. |
|
|
| |
|
|
| <pre>package org.apache.synapse.config.xml; |
| |
| import ... |
| |
| /** |
| * Creates a Spring configuration extension from XML configuration. A Spring |
| * configuration extension keeps Spring away from the core of synapse |
| * |
| * <spring:config name="string" src="file"/> |
| */ |
| public class SpringConfigExtensionFactory implements ExtensionFactory { |
| |
| private static final Log log = LogFactory.getLog(SpringConfigExtensionFactory.class); |
| |
| private static final QName SPRING_CFG_Q = new QName(Constants.SYNAPSE_NAMESPACE + "/spring", "config"); |
| |
| /** |
| * <spring:config name="string" src="file"/> |
| * |
| * @param elem the XML configuration element |
| * @return A named Spring Configuration |
| */ |
| public Extension createExtension(OMElement elem) { |
| |
| SpringConfigExtension springCfgExt = null; |
| OMAttribute name = elem.getAttribute(new QName(Constants.NULL_NAMESPACE, "name")); |
| OMAttribute src = elem.getAttribute(new QName(Constants.NULL_NAMESPACE, "src")); |
| |
| if (name == null) { |
| handleException("The 'name' attribute is required for a Spring configuration definition"); |
| } else if (src == null) { |
| handleException("The 'src' attribute is required for a Spring configuration definition"); |
| } else { |
| springCfgExt = new SpringConfigExtension(name.getAttributeValue(), src.getAttributeValue()); |
| } |
| return springCfgExt; |
| } |
| |
| private void handleException(String msg) { |
| log.error(msg); |
| throw new SynapseException(msg); |
| } |
| } |
| </pre>
|
| The getTagQName() returns the name space
|
| "http://ws.apache.org/ns/synapse/spring" and the element name as
|
| "config", and thus registers itself as the handler for a
|
| <spring:config .../> element where the namespace spring refers to
|
| "http://ws.apache.org/ns/synapse/spring". It should be noted that the
|
| XML configuration extensions must reside within the <definitions>
|
| elements of the Synapse XML configuration. Refer to the Synapse
|
| configuration language syntax for more information on the structure of
|
| the Synapse XML. If the Synapse configuration builder comes across an
|
| XML configuration element that has been registers (as shown above), it
|
| will then call into the implementation of the ExtensionFactory
|
| implementations' createExtension(OMElement elem) method with the XML
|
| element, and an instance of the Extension interface implementation in
|
| return. This extension implementation class getName() method is used to
|
| store it into the SynapseConfiguration, as a "property", and thus could
|
| be retrieved later via this same "name". Hence the implementations
|
| should be careful to use unique meaningful names for their
|
| implementations. |
|
|
| |
|
|
| For completeness, the implementation of the Spring extension is
|
| given in the following listing. |
|
|
| |
|
|
| <pre>package org.apache.synapse.config; |
| |
| import ... |
| |
| /** |
| * This defines an extension to Synapse to process a Spring Configuration. |
| * This keeps the Spring dependency out from the Synapse core, and the |
| * dependent Jars from the core distribution. |
| * |
| * A Spring configuration is usually named, but this class allows an |
| * inlined configuration to be built up as well, where the Spring mediator |
| * defines an inline Spring configuration |
| */ |
| public class SpringConfigExtension implements Extension { |
| |
| /** |
| * The name of this Spring configuration |
| */ |
| private String name = null; |
| |
| /** |
| * This is the Spring ApplicationContext/BeanFactory |
| */ |
| private GenericApplicationContext appContext = null; |
| |
| /** |
| * Create a Spring configuration from the given configuration |
| * |
| * @param configFile the configuration file to be used |
| */ |
| public SpringConfigExtension(String name, String configFile) { |
| setName(name); |
| appContext = new GenericApplicationContext(); |
| XmlBeanDefinitionReader xbdr = new XmlBeanDefinitionReader(appContext); |
| xbdr.setValidating(false); |
| xbdr.loadBeanDefinitions(new FileSystemResource(configFile)); |
| appContext.refresh(); |
| } |
| |
| public GenericApplicationContext getAppContext() { |
| return appContext; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public QName getTagQName() { |
| return SPRING_CFG_Q; |
| } |
| } |
| |
| </pre>
|
| </body>
|
| </html>
|