[ https://issues.apache.org/jira/browse/TUSCANY-4073?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13506620#comment-13506620 ]
Sebastian Millies commented on TUSCANY-4073: -------------------------------------------- I have solution of sorts, and will attempt to summarize it here, working in some of Simon's comments as well. To re-iterate the problem: I want to set up my contributions, so that they can use different versions of third-party libraries than the Tuscany 1.6 runtime. My original example was using recent versions of EMF, another example is using Tuscany with an Apache Solr 4.0 backend, which requires different Apache Http Components. Some approaches have turned out to be infeasible: 1) patching the built-in ContributionClassLoader to use a parent-last strategy leads to several errors in Tuscany tests and samples. The fault may lie with the tests being based on wrong assumptions and bad-practices, but to correct them all is not a practical option. (Besides, it would be contrary to the OASIS specs.) 2) The approach outlined in [1] is quite complicated, and I have not been able to get it to work. Instead, I have chosen to exploit the Tuscany extension mechanism in order to provide a class loader of my choice as the contribution class loader. This solution will work when one follows the recommended best practice of keeping contribution classes separate from the system classpath. The new class loader is the delegation parent of the contribution class loader. The approach has two limitations: 1) the contributions shouldn't pass any references to these overriding external libraries to the Tuscany runtime (e. g. in the form of service parameters) 2) the extension mechanism is a applied globally rather than to specific contributions. (Both these points do not worry me. With regard to the second one, the overriding classes could be included in a nested jar in the contribution itself, so even if the mechanism is global, any overriding could be kept strictly local, if desired. After all, it's my own class loader doing the work.) Finally, here's how it really works, without requiring any code changes in Tuscany itself. 1) Define a class loader, e. g. EndorsedLibsClassLoader extends URLClassLoader 2) Define a class loader provider, e. g. EndorsedLibsClassLoaderProvider implements ContributionClassLoaderProvider. This is the core of the solution. We inject the new class loader between the standard contribution class loader and the app class loader: public ClassLoader getClassLoader( Contribution contribution, ClassLoader parent ) { EndorsedLibsClassLoader endorsedLoader = new EndorsedLibsClassLoader( parent ); return new ContributionClassLoader( contribution, endorsedLoader ); } 3) Define a class loader provider extension point, e. g. PrioritizedContributionClassLoaderProviderExtensionPoint implements ContributionClassloaderProviderExtensionPoint The reason for this is that Tuscany will look up ALL class loader providers, not just your own, then put them in a HashSet, iterate over the set, and keep one provider per contribution type, whichever one it happens to find last (clearly a bug, in my view). The PrioritizedContributionClassLoaderProviderExtensionPoint allows class loader providers to have a "priority" attribute, and keeps the one with the highest priority. 4) Register the provider and the provider extension point in two files in a META-INF/services directory. The file names are: org.apache.tuscany.sca.contribution.java.ContributionClassLoaderProvider org.apache.tuscany.sca.contribution.java.ContributionClassloaderProviderExtensionPoint The latter just contains the FQCN of the extension point implementation. The former contains the FQCN of the class loader provider, along with its attributes, e. g. like this: example.contribution.java.EndorsedLibsClassLoaderProvider;type=application/x-compressed,priority=10 5) Put the META-INF directory on the system classpath. But not just anywhere: It must come BEFORE the Tuscany runtime, because the tuscany-sca-all.jar will contain an analogous directory, and the extension point is loaded only once, from the first text file with appropriate name that is found. This is really quite simple, after all. It may not be the best of all possible solutions, but it works for me. -- Sebastian PS: I'd like to share some example code (99,9% is original Tuscany code anyway...) I have put the text of this mail plus a zip archive into the JIRA that has occasioned this entire exchange: https://issues.apache.org/jira/browse/TUSCANY-4073 [1] http://mail-archives.apache.org/mod_mbox/tuscany-user/201006.mbox/%3c4c164dd3.8090...@apache.org%3E [2] https://cwiki.apache.org/TUSCANYWIKI/classloading.html > Change classloader to use different versions of third-party libraries than > the Tuscany runtime > ---------------------------------------------------------------------------------------------- > > Key: TUSCANY-4073 > URL: https://issues.apache.org/jira/browse/TUSCANY-4073 > Project: Tuscany > Issue Type: Improvement > Components: SCA Java Runtime > Affects Versions: Java-SCA-1.6.2 > Environment: Java 7 on Windows 7 (64bit) > Reporter: Sebastian Millies > Fix For: Java-SCA-1.6.2 > > Attachments: classloader.patch > > > I want to set up my contributions, so that they can use > different versions of third-party libraries than the Tuscany runtime. My > original example > was using recent versions of EMF (hence the subject line), another example is > using > Tuscany with an Apache Solr 4.0 backend, which requires different Apache Http > Components. > The standard recommendation is [1], but I have had great trouble to get that > to work. (The > reasons have to do with the use of SDOs in the application in question.) > I have therefore decided to try the opposite approach of including any > different versions of components > used by Tuscany in nested jars in the contribution itself. Nested jars in a > zip contribution get added into > the contribution classpath. > Here I am working under the assumption that the SCA contribution classloader > would work > somewhat like a webapp class loader in that it would not follow the > delegation model, > but would look for classes in the following order > 1) inside the contribution > 2) in the imports > 3) in the parent classloader > With this behavior, everything goes well. For example, I can make calls to > Apache Solr through > the solr-solrj-4.0.0.jar and its dependents, including httpclient-4.1.3.jar > and httpcore-4.1.4.jar, > without impacting HTTP calls made by Tuscany-generated proxies elsewhere. > Here's the snag: As it turned out, > org.apache.tuscany.sca.contribution.java.impl.ContributionClassLoader DID > NOT work the way I expected, but rather looked in the parent classloader > first, only then inside the contribution. > I had to change the coding (in module contribution-java) and the associated > test. A patch is attached. > Would my change break anything, perhaps with respect to OSGi? Is there > anything in the SCA spec that mandates a > certain class loading behavior? I do feel that the alternative behavior is > more natural than the one that is currently > implemented. (There a very few resources on Tuscany classloading, and e. g. > [2] does not seem to mention > this particular issue.) > Unfortunately, I cannot get all the Tuscany 1.6 tests to compile and run > with maven. > Please, would anyone be willing to see if Tuscany 1.6 with my patch applied > would still pass all current tests? > (unless my proposal is obviously wrong for other reasons, of course) > Best, > Sebastian > [1] > http://mail-archives.apache.org/mod_mbox/tuscany-user/201006.mbox/%3c4c164dd3.8090...@apache.org%3E > [2] https://cwiki.apache.org/TUSCANYWIKI/classloading.html -- This message is automatically generated by JIRA. If you think it was sent incorrectly, please contact your JIRA administrators For more information on JIRA, see: http://www.atlassian.com/software/jira