The XML used for the SCA Assembly model is structurally quite simple but very extensible; almost every element support extension elements and the specification itself uses substitution groups to allow specific types of implementation and binding to replace/extend key elements. Further, the model itself makes reference to artifacts located outside the XML instance (such as WSDL definitions, Java classes and other SCA artifacts like componentType sidefiles) that are needed to build up the complete representation of an application.

To handle this extensibility and the external artifacts, in Tuscany we have chose to use a streaming approach to parsing based on the Java StAX framework. Rather than load XML artifacts into objects using a data binding technology such as SDO or JAXB, the loader framework provides a mechanism for extension to register their willingness to parse any XML element. As the XML file is read, elements are dispatched to registered loaders who can then handle the stream in any way they choose. As part of processing the stream, they can also read any associated artifacts especially those that may be needed later during the configuration loading process (such as WSDL definitions).

To partipate in the loading process, an extension should provide a component that implements the StAXElementLoader interface and should register that component with the framework's StAXLoaderRegistry during initialization.

Using the JavaScript implementation type as an example, this would be something like:

@Scope("MODULE")
public class JavaScriptImplementationLoader implements StAXElementLoader<JavaScriptImplementation> {
    public static final QName IMPLEMENTATION_JS = new QName("http://org.apache.tuscany/xmlns/js/0.9", "implementation.js");

    private StAXLoaderRegistry registry;

    @Autowire
    public void setRegistry(StAXLoaderRegistry registry) {
        this.registry = registry;
    }

    @Init(eager = true)
    public void start() {
        registry.registerLoader(IMPLEMENTATION_JS, this);
    }

    @Destroy
    public void stop() {
        registry.unregisterLoader(IMPLEMENTATION_JS, this);
    }

In this example, an instance of this component would be included with the extension. When the extension module starts, the component is initialized immediately due to the presence of the @Init(eager = true) annotation. In its init method, it registers itself with the loader registry as a loader for elements with the QName <implementation.js> in the JavaScript extension's namespace.

As XML configuration files are being parsed, when the loader sees the registered element it will call this component's load method to handle it. Continuing the JavaScript example, we see:

    public JavaScriptImplementation load(XMLStreamReader reader, ResourceLoader resourceLoader) throws XMLStreamException, ConfigurationLoadException {
        String scriptFile = reader.getAttributeValue(null, "scriptFile");
        String style = reader.getAttributeValue(null, "style");
        String script = loadScript(scriptFile, resourceLoader);
        ComponentInfo componentType = loadComponentType(scriptFile, resourceLoader);

        JavaScriptImplementation jsImpl = factory.createJavaScriptImplementation();
        jsImpl.setComponentInfo(componentType);
        jsImpl.setScriptFile(scriptFile);
        jsImpl.setStyle(style);
        jsImpl.setScript(script);
        jsImpl.setResourceLoader(resourceLoader);
        return jsImpl;
    }

The load method is called positioned on the element to be handled (<implementation.js>) so that all attributes and content can be handled. The load method is responsible for parsing the XML stream and returning an appropriate AssemblyObject (in this case a JavaScriptImplementation). In this example, the loader is using the stream directly but it could just as easily use the data binding library of its choice. When the method returns, the reader should be positioned on the matching element end event.

In this case, the JavaScript implementation needs to access two external resources that are not part of the XML file. The source code for the script is loaded in the loadScript() method and stored as a property in the configuration model. Similarly the component definition is loaded from a .componentType sidefile by the loadComponentType() method and also stored in the model. The resulting Implementation object is thereby complete and the builder can create the final component without having to load external resources.

To add the loader to the runtime configuration a <component> definition is added in a SCA Assembly file:

<moduleFragment 
        xmlns="http://www.osoa.org/xmlns/sca/0.9"
        xmlns:system="http://org.apache.tuscany/xmlns/system/0.9"
        name="org.apache.tuscany.container.js">
...
    <component name="org.apache.tuscany.container.js.loader.JavaScriptImplementationLoader">
        <system:implementation.system class="org.apache.tuscany.container.js.loader.JavaScriptImplementationLoader"/>
    </component>
...
</moduleFragment>

The extension is added to the system by including this fragment on the classpath as a resource with the name system.fragment.

Summary

Loaders handle extensions' elements contained in SCA Assembly files. A loader is an implementation of StAXElementLoader that registers itself with the runtime's StAXLoaderRegistry. Extensions contribute loaders by including a <component> definition in a system.fragment file on their classpath.

Tuscany/TuscanyJava/Extending/StAXLoading (last edited 2009-09-20 22:47:33 by localhost)