Aims

We are trying to improve the way Synapse can load XML and configuration from more than just synapse.xml.

  1. Want to be able to pull config from more than one file.
  2. Want to be able to name a source of one or more XMLs (a "registry"), and be able to easily switch between say "test" and "production", and have many links change automatically
  3. Want to be able to have a "dynamic" config and re-read without restarting Synapse. Of course we will implement caching to make sure its not really slow.

"Registry"

Our first "registry" will just be a URL source like a webserver. The difference is that you will specify a "root" URL prefix plus paths. Of course we'll make the registry interface pluggable so that you can also deal with other real registries, or even UDDI :-)

Using the model in synapse.xml

First pass design:

We already have an internal naming system in Synapse, which is a hashtable bootstrapped by the definitions section. We can think of this as an in-memory registry. We can then back this up with an external registry, so if you don't find something in the memory one, you look it up in the external registry. We thought about having multiple registries, but figured a good starting point was just one external registry, and if we need more later we can add support then.

The external registry is defined by an interface and you can configure the provider using the following XML syntax:

<syn:registry provider="provider.class">
  <property name="prop-for-Registry" value="...">
</syn:registry>

Our first URL based HTTP registry will look like this:

<syn:registry provider="o.a.s.r.HTTPRegistry">
  <property name="root" value="http://localhost:80/myxmls">
</syn:registry>

At the moment the properties in our config

<property name="" value=""/>

are

  1. only strings
  2. only loaded from this XML (i.e. not pulling in an external resource)

We will change this:

Instead of having each mediator pull XML config or metadata in its own way, we can extend the properties to support XML, and then have the mediators grab the XML from a property. The property can either be in memory or a key to the external registry.

So in effect we will funnel any access to external XMLs through the "registry" model. We will remove any "src" tags from mediators and instead have them reference XML through the property / registry model.

However, we will still expect mediators to allow inline XML "right there" so as to make the structure easy and clear. So an XSLT can be loaded from a file, in which case its specified at the top using a property; or inlined, when it can be either in a property or in the actual <xslt/> element.

mediator <== property <== (value | inline | src | lookup)

So here is the enhanced property xml structure. It must have exactly one of value, src, key or inline:

 <property name="foo" [value="string"] [src="url to XML"] [key="string-key"]>
   <inline-xml/>
 </property>

Note that the particular approach of <property name="" key=""/> is really just an aliasing mechanism - its setting up a link between an internal key and an external key into the external registry. And if you specify a key then it will lookup the entry *whenever* you use it. Of course we will optimize cache etc this.

So now if I specify

 <property name="stockxsl" key="/xsls/stockquote.xsl"/>

Then together with the registry definition above, this will load the XSL from http://localhost:80/myxmls/xsls/stockquote.xsl and associate it with the local name stockxsl.

I can now use this xsl like this:

<transform xslt="stockxsl"/>

This will be dynamic, so if I update the file at http://localhost:80/myxmls/xsls/stockquote.xsl it will be picked up and used by Synapse without restarting.

Internal Registry API

The core in-memory registry is actually the SynapseEnvironment properties model. The other registry is just a mapping from keys (strings) to XML fragments (OMNode).

SynapseEnvironment {
   ...
   Object getProperty(String name);
   ...
   XMLRegistry getXMLRegistry();
   ...
}

public interface XMLRegistry {
  OMNode lookup (String key);
  OMNode lookupIfModified (String key, long since);
  void insert (key, OMNode);
}

Efficiency

We tried to get the "user model" right first, so now we'd like to make sure its efficient.

In order to make this model efficient, we need to be able to cache objects: Imagine you have an XSLT that you are looking up from the registry. You want to be able to compile the XSLT only once. When someone asks for it again, you want to check if its been modified, and only recompile if it has.

So there are two efficiencies involved here:

  1. Fetching the XSL from the URL. Make sure we use GetIfModified etc, and maybe set a timer locally so we don't even check IfModified if I checked in the last n seconds.

  2. Compiling the XSLT - if the XML/XSLT code hasn't changed we shouldn't recompile it.

Internally we figured we could handle the first by having the "lookupIfModified" method on the registry provider API.

For the second aspect we will maintain a list of "converters" which can convert from the XML to an object. If a given converter is registered, then we always run it on the XML before returning the property. So here is the pseudo-code for getProperty

public class DynamicProperty {
   Mapper xmlMapper;
   Object cache;
   long timeCached;
   String key;
}

SynapseEnvironment.getProperty(String key) {
    Object obj = hashtable.lookup(key);
    if (obj instanceof DynamicProperty) {
         XMLRegistry r = this.getRegistry;      
         DynamicProperty dp = (DynamicProperty)obj;
         OMElement el = null;
         //have we ever cached this?
         if (dp.cache==null) {
             el = r.lookup(dp.key);
         } else 
             el = r.lookupIfModified(dp.key, timeCached);
             if (el == null) return dp.cache 
         }

         if (dp.xmlMapper==null) dp.cache = el;
         else dp.cache = dp.xmlMapper.fromOM(el);
         dp.timeCached = now;      
         return dp.cache;
    }
    return obj;
}

SynapseEnvironment.registerMapper(String key, Mapper mapper);

So the XSL mediator can register its compiler with the DynamicObject