The Nature of the Tuscany Runtime

The Tuscany runtime comprises a set of core runtime classes that in turn load extensions to provide SCA functionality. This combination of core and extension classes is used to process contributions and load and run composite applications.

The classloading strategy applied to core and extension classes (runtime classloading) differs from that applied to load application artefacts from contributions. Application artefacts are presented in a set of related contributions and classloading must operate in this context (contribution classloading)

Extensions may also have a direct impact on classloading, for example, the use of OSGi to either bundle Tuscany runtime classes or to provide a component implementation implies a different approach to classloading based on OSGi bundles and the relationships defined between bundles.

Runtime Classloading

The classloader used to load the Tuscany runtime itself. It should be able to
locate all Tuscany jar files and all its dependent jar files (eg. axis2).

Requirements?

Tuscany runtime classloader should find Tuscany and all its dependencies (essentially tuscany-sca-manifest.jar).

To enable third party libraries to see Tuscany classes the thread context classloader should be able to see Tuscany's dependencies and also Tuscany classes - so this can be the same as (or a child of) the Tuscany runtime classloader. Not desireable but apparently unavoidable.

It should be usable in a number of different hosting environments

Handle modularity, for example, here is a structure that Rajini investigated.

1. SCA API (sca-api)
2. Tuscany SPI (core-spi, assembly, contribution, policy, interface, etc.)
3. Tuscany Runtime ( assembly-xml, assembly-xsd,etc.)
4. Tuscany Extensions (binding-. implementation-, host-*,
databinding-*, contribution-osgi)
5. 3rd Party Libraries (Axis2 etc)

Contributions are loaded separately

Command Line

For command line operation the core and extension Tuscany classes are treated as user-defined classes and must be provided directly on the classpath, for example,

Java -classpath tuscanydistribution/lib/Tuscany-sca-manifest.jar ...

At startup the hosting environment will start the core runtime and load any extension classes that are found using the classpath based classloader.

WebApp

Webapp operation is similar principle but the location of the Tuscany jars affect which classloader loads then. For example, the jar could be deployed with each web application or could be deployed into a directory common to all web applications.

OSGi

The current OSGi runtime bundling (itest/osgi-tuscany) splits Tuscany into the aforementioned bundles.

1. SCA API (sca-api)
2. Tuscany SPI (core-spi, assembly, contribution, policy, interface, etc.)
3. Tuscany Runtime ( assembly-xml, assembly-xsd,etc.)
4. Tuscany Extensions (binding-. implementation-, host-*,
databinding-*, contribution-osgi)
5. 3rd Party Libraries (Axis2 etc)

The felix runtime is started, these bundles are loaded using Felix APIs and the Tuscany runtime is started with the following code (org\apache\tuscany\sca\test\util\TuscanyLoader)

        if (tuscanyRuntimeBundle != null) {
            tuscanyRuntimeBundle.start();
            
            // Tuscany runtime is started on a different thread when previously cached bundle is used.
            // Set this thread's TCCL to the one used by the runtime.
            try {
                Class<?> runtimeClass = tuscanyRuntimeBundle.loadClass("org.apache.tuscany.sca.osgi.runtime.OSGiRuntime");
                Method getRuntimeMethod = runtimeClass.getMethod("findRuntime");
                Object runtime = getRuntimeMethod.invoke(runtimeClass);
                Method getTCCLMethod = runtimeClass.getMethod("getContextClassLoader");
                ClassLoader runtimeTCCL = (ClassLoader) getTCCLMethod.invoke(runtime);
                Thread.currentThread().setContextClassLoader(runtimeTCCL);
                
                
            } catch (Throwable e) {
            }
        }

The classloading then relies on the OSGI classloader structure to resolve references across bundles.

What are the issues here now?
Modularization
TBD

Contribution Classloading

Application artifacts such as .composite, .java. and .wsdl files are provided in one or more related contributions. Consider the following;

ContributionA
compositeA.composite
implementation/ComponentA.java

ContributionB
implementation/ComponentB.java

Where compositeA.composite is a follows;

<composite ...>
    <component name="ComponentA">
        <implementation.java class="implementation.ComponentA"/>
    </component>

    <component name="SubtractServiceComponent">
        <implementation.java class="implementation.ComponentB"/>
    </component>
</composite>

Contribution processing reads each contribution into an in-memory artefact model during a read phase and resolves the relationships between artefacts during a subsequent resolve phase.

The model resolved classes are closely associated with classloading as to resolve a java artefact a class must be loaded.

Requirements?

Classloader Visibility - restricted to the transitive closure of all of the classes from the contribution import/export.java dependency graph + anything on the runtime classpath

One classloader per contribution? With access to all classloaders from contributions that it imports from.

Cycles in imports/exports should be detected (general resolution requirement)

Handle packages split across multiple contributions

Must equally support explicit resolution and automatic triggering from the VM

Use contributions without providing a classloader

Pluggability of classloader strategies

Notes on Proposed Approach

The runtime is not quite like this yet but this is what appears to be favoured from the various mail list discussions.

Model Resolution
For each artefact in contribution
Find a processor for the artefact type to resolve the artefact
At appropriate points call the model resolvers., e.g. implementation.java resolver
ClassReferenceModelResolver is constructed with a reference to the contribution.

Hence for classloading initiated by Tuscany

ExtensibleModelResolverA->ContributionClassLoaderA(implements ModelResolver>->ContributionClassLoaderB(implements ModelResolver)

For classloading initiated by the application

ConributionClassLoaderA->ContributionClassLoaderB

Issues with the current code

There is currently a separate resolver and contribution class loader hierarchy but there is a proposal to join them together by having the java resolvers extend classloader.

Listeners, Resolvers and ContributionClassloader are involved in processing imports/export.java in some way which could be simplified.

ContribtutionListeners -  Set up contribution processing based on events
    ContributionListener
         ContributionAdded/Updated/Removed
    NamespaveImportExportListener 
-	sets import model resolver to model resolver from contribution with export
    ResourceImportExportListener
-	sets import model resolver to model resolver from contribution with export
    JavaImportExportListener 
-	sets import model resolver to JavaImportModelResolver
Resolvers
  ExtensibleModelResolver
  ClassReferenceModelResolver
    OSGiClassReferenceModelResolver (delegated to for bundle contributions)
  Etc.
Processors
  ExtensibleURLArtifactProcessor
  CompositeDocumentProcessor
  ExtensibleStAXArtifactProcessor
  CompositeProcessor
  JavaImplementationProcessor
  Etc.

References

These notes are mainly distilled from the following discussions

http://www.mail-archive.com/tuscany-dev@ws.apache.org/msg28235.html http://www.mail-archive.com/tuscany-dev@ws.apache.org/msg25100.html http://www.mail-archive.com/tuscany-dev@ws.apache.org/msg24774.html http://cwiki.apache.org/confluence/display/TUSCANYWIKI/Classloading+in+Tuscany+SCA+Java http://cwiki.apache.org/confluence/download/attachments/68801/classloader-dependencies.png http://cwiki.apache.org/confluence/download/attachments/68801/desired-classloader.png

  • No labels