Space: Apache Tuscany Docs 2.x
(https://cwiki.apache.org/confluence/display/TUSCANYxDOCx2x)
Page: Classloading
(https://cwiki.apache.org/confluence/display/TUSCANYxDOCx2x/Classloading)
Edited by kelvin goodson:
---------------------------------------------------------------------
Need to detail the various ways the Tuscany runtime loads classes and the
issues involved. Here are some notes to kick of this document
h1. Classloading Objectives
The runtime must work in both OSGi and non-OSGI environments. I.e. we can't
rely on the OSGi service registry for extensibility
The runtime must not be generally environment specific. I.e. no buddy
classloading
...
h1. General Patterns
h2. Extension loading - JSE
tuscany-extensibility
Tuscany finds extensions by looking for META-INF/services files on the
classpath.
h2. Extension loading - OSGi
tuscany-extensibility-equinox
It's a bit more complicated here. The extensibility-equinox bundle is given the
entire OSGi context at start up and from there is looks in all of the loaded
bundles looking for META-INF/services files. It caches them against the bundle
in which they are found.
The tuscany-extensibility-equinox bundle also has a dynamic import
{code}
DynamicImport-Package: org.apache.tuscany.sca.extensibility.equinox,
javax.transaction;version="1.1",
javax.transaction.xa;version="1.1",
{code}
Which allows it to generally load any classes in the runtime
h2. Split Packages - JSE
We don't take any special account of this in JSE (?)
We avoid split packages across the JARs we create as it messes OSGi up.
h2. Split Packages - OSGI
We avoid split packages across the bundles we create
They may exist in third party bundles (or jars that we turn into bundles) so we
need a way round it
The Tuscany eclipse plugin is used to generate bundles manifest for jars which
don't have them. This is done automatically with all packages exported (?) and
the resulting bundle it in the distribution modules directory in the following
form
{code}
bundle-name
META-INF
MANIFEST.MF
bundle-name.jar
{code}
The MANIFEST.MF is generated and will have a bundle classpath pointing to the
jar (which doesn't itself have a manifest
The runtime (node-launcher-equinox) has code to load these directories as
bundles.
There is a way of overriding these automatically generated bundles so that
split packages (or any other manifest problems) can be worked round. Generate
the manifest manually and put it in
{code}
distribution/all/manifests
{code}
Update distribution/pom.xml to configure the Tuscany version of the maven
bundle plugin to apply this manifest
{noformat}
<plugin>
<groupId>org.apache.tuscany.maven.plugins</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>1.0.6</version>
<executions>
<execution>
<id>distribution-modules</id>
<phase>generate-resources</phase>
<goals>
<goal>generate-modules</goal>
</goals>
<configuration>
<targetDirectory>target/modules</targetDirectory>
<useDistributionName>${useDistributionName}</useDistributionName>
<generateAggregatedBundle>${generateAggregatedBundle}</generateAggregatedBundle>
<generateManifestJar>true</generateManifestJar>
<artifactManifests>
<artifactManifest>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-api</artifactId>
<version>1.2.8</version>
<manifestFile>${basedir}/manifests/axiom-api-1.2.8.MF</manifestFile>
</artifactManifest>
<artifactManifest>
<groupId>org.apache.woden</groupId>
<artifactId>woden-impl-dom</artifactId>
<version>1.0M8</version>
<manifestFile>${basedir}/manifests/woden-impl-dom-1.0M8.MF</manifestFile>
</artifactManifest>
<!--artifactManifest>
<groupId>org.apache.tuscany.sdo</groupId>
<artifactId>tuscany-sdo-api-r2.1</artifactId>
<version>1.1.1</version>
<manifestFile>${basedir}/manifests/tuscany-sdo-api-r2.1-1.1.1.MF</manifestFile>
</artifactManifest-->
<!-- artifactAggregations (below) is the right
approach to solving the split
package between axis-kernel and
axis2-transport-http however the Tuscany
runtime doesn't take any notice of it so
using a fragment at the moment -->
<artifactManifest>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.5.1</version>
<manifestFile>${basedir}/manifests/axis2-kernel-1.5.1.MF</manifestFile>
</artifactManifest>
<artifactManifest>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>1.5.1</version>
<manifestFile>${basedir}/manifests/axis2-transport-http-1.5.1.MF</manifestFile>
</artifactManifest>
<artifactManifest>
<groupId>org.apache.axis2</groupId>
<artifactId>*</artifactId>
<version>*</version>
</artifactManifest>
</artifactManifests>
<!--artifactAggregations>
<artifactAggregation>
<symbolicName>org.apache.tuscany.sca.axis2-kernel</symbolicName>
<version>1.5.1</version>
<artifactMembers>
<artifactMember>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.5.1</version>
</artifactMember>
<artifactMember>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>1.5.1</version>
</artifactMember>
</artifactMembers>
</artifactAggregation>
</artifactAggregations-->
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>osgi</artifactId>
<version>3.3.0-v20070530</version>
</dependency>
</dependencies>
</plugin>
{noformat}
You'll note that there is an artifact aggregation element that doesn't work at
the moment. This should aggregate the two bundles together so that a split
package isn't an issue. As this doesn't work at the moment another way to
achieve the same result is to make one package a fragement of the other by
configuring separate manifests manually.
NOTE\!\!\!\!\! you also need to put the manually generated manifest in
node-launcher-equinox\src\main\resources\org\apache\tuscany\sca\node\equinox\launcher
otherwise you'll spend a lot of time trying to get this to work. (we need to
fix this\!)
h2. Third-party libraries - JSE
TBD
h2. Third-party libraries - OSGI
Third-party libraries often rely on TCCL to load implementation classes in an
extensible way. For example, the SDO API loads the HelperContext implementation
in this way. In an OSGi environment there will not be a static dependency
between the api bundle and the impl bundle so we need to fake it. Typically we
do this by setting up the TCCL appropriately before the library us called.
See ClassLoaderContext which help us to set up a multi-classloader
configurations.
Typically in OSGi one of the classloaders we pass in here will be the
extensibiliy-equinox bundle classloader (the ServiceDiscoverer) as this bundles
has a dynamic import which allows it to load any class in the runtime.
h2. Tuscany Node API - JSE
TBD
h2. Tuscany Node API - OSGi
There are a small number of Tuscany Jars you need to use in the app launcher in
the OSGi environment
tuscany-sca-api
tuscany-node-api
tuscany-node-launcher-equinox
The node API has to load the node implementation and has a dynamic import in
its manifest
DynamicImport-Package:
org.apache.tuscany.sca.node.impl,org.apache.tuscany.sca.extensibility
h2. SCA Client API - JSE
Factory finder impl is injected into the API class by the implementation
h2. SCA Client API - OSGi
NodeFactory maintains a NodeProxy inner class that supports cross-classloader
calls. The calling client api will have been loaded by the app classloader but
the underlying node will have been loaded by a bundle classloader. We need to
bridge that gap.
h2. Contribution Class Loading
observations on contribution class loading .... these will need distilling down
when I have made some real conclusions
The ClassLoaderModelResolver (CLMR) specializes java.net.URL.URLClassLoader and
implements o.a.t.s....ModelResolver.
*In a simple scenario* example (see SampleJSELauncher): in
ClassLoaderModelResolver::resolveModel() where for example the ClassReference
instance names "calculator.CalculatorServiceImpl" as an unresolved Java class.
This method does a Class.forName on the named element and thereby relies on the
URLClassLoaders defineClass behaviour. (see note below on parent class
loader determination)
*In a more complex scenario* where imports and exports exist between
contributions, the itest project import-export-tests has a class
TestTestCase with method testOneNode. In this example a node is created
using 2 composite URIs for contributions ... "../exports/target/classes",
"../imports/target/classes"
In the sca-contribution.xml of the exports project,
the org.apache.tuscany.sca.itest.exports package is exported, and is
similarly imported in the imports project. The Export project exports the
interface HelloWorld. The import project provides the HelloWorldImpl
implementation of the interface provided by the exports project.
In starting the node, the default deployer resolves contribution dependencies.
The contribution Content Processor resolves the
HelloWorld.composite artifact
The extensible StAX artifact processor looks inside the
composite file
The composite processor amongst other things
resolves the component implementation, in this case HelloWorldImpl.class
The Java Implementation Processor
names the class in an instance of oats.contribution.resolver.ClassReference
The
ExtensibleModelResolver determines that the ClassLoaderModelResolver is the
thing to resolve a java implementation artifact (so far this is just like the
simple scenario)
The ContributionURLs are determined to be the classes iunder the target folder
of the imports project
The parent class loader for the new ClassLoaderModelResolver is determined by
the ServiceDiscovery singleton to be the thread context class loader (check to
see if this is the same in the simple scenario \-\- i think so)
The ClassLoaderModelResolver observes that the contribution has an import of
the oats.xxx.exports package
it
discovers it has no mapping for that package and creates a placeholder for such
a mapping in the map which it holds in its state in importResolvers
The ExtensibleModelResolver asks the CLMR to resolve the HelloWorldImpl
class
The CLMR
calls Class.forName on the impl class, suppying itself as the class loader
The Class.forName call results in the CLMR's findClass method being called
The CLMR uses its importResolvers to attempt to resolve the class, but fails
so it attempts to resolve the class from the current contribution (via the
superclass function forName()) which is url based)
in doing so we hit the CLMR's
findClass method again, but instead looking for the HellowWorld interface of
the exports package
Change your notification preferences:
https://cwiki.apache.org/confluence/users/viewnotifications.action