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