Need to detail the various ways the Tuscany runtime loads classes and the issues involved. Here are some notes to kick of this document Classloading ObjectivesThe runtime must work in both OSGi and non-OSGI environments. I.e. we can't rely on the OSGi service registry for extensibility General PatternsExtension loading - JSEtuscany-extensibility Extension loading - OSGituscany-extensibility-equinox The tuscany-extensibility-equinox bundle also has a dynamic import DynamicImport-Package: org.apache.tuscany.sca.extensibility.equinox, javax.transaction;version="1.1", javax.transaction.xa;version="1.1", Which allows it to generally load any classes in the runtime Split Packages - JSEWe don't take any special account of this in JSE Split Packages - OSGIWe avoid split packages across the bundles we create 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 bundle-name META-INF MANIFEST.MF bundle-name.jar 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 distribution/all/manifests Update distribution/pom.xml to configure the Tuscany version of the maven bundle plugin to apply this manifest <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> 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!) Third-party libraries - JSETBD Third-party libraries - OSGIThird-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. Tuscany Node API - JSETBD Tuscany Node API - OSGiThere are a small number of Tuscany Jars you need to use in the app launcher in the OSGi environment tuscany-sca-api 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 SCA Client API - JSEFactory finder impl is injected into the API class by the implementation SCA Client API - OSGiNodeFactory 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. Contribution Class Loadingobservations 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
|