/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.tuscany.sca.contribution.impl; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import org.apache.tuscany.sca.contribution.Contribution; import org.apache.tuscany.sca.contribution.Export; import org.apache.tuscany.sca.contribution.Import; import org.apache.tuscany.sca.contribution.java.JavaImport; import org.apache.tuscany.sca.contribution.namespace.NamespaceImport; public class ContributionClassLoader extends URLClassLoader { private Contribution contribution; /** * Constructor for contribution classloader * * @param contribution * @throws MalformedURLException */ public ContributionClassLoader(Contribution contribution) { // To enable contributions to access code outside of SCA contributions // (typically by providing them on CLASSPATH), use the thread context // classloader as the parent of all contribution classloaders. super(new URL[0], Thread.currentThread().getContextClassLoader()); this.contribution = contribution; } /** * Add the URL of the contribution to the classloader search path. * * @param location Contribution URL */ public void setContributionLocation(String location) { try { this.addURL(new URL(contribution.getLocation())); } catch (MalformedURLException e) { throw new RuntimeException(e); } } /* (non-Javadoc) * @see java.net.URLClassLoader#findClass(java.lang.String) * * Search path for class: * This contribution * Imported contributions */ @Override protected Class findClass(String className) throws ClassNotFoundException { Class clazz = null; try { clazz = findClassFromContribution(className); } catch (ClassNotFoundException e) { for (Import import_ : this.contribution.getImports()) { if (matchesImport(className, import_, true)) { // Delegate the resolution to the imported contribution for (Contribution exportingContribution : import_.getExportContributions()) { if (exportingContribution.getClassLoader() instanceof ContributionClassLoader) { for (Export export : exportingContribution.getExports()) { try { if (import_.match(export)) { clazz = ((ContributionClassLoader)exportingContribution.getClassLoader()).findClassFromContribution(className); break; } } catch (ClassNotFoundException e1) { continue; } } if (clazz != null) break; } } if (clazz != null) break; } } if (clazz == null) throw e; } return clazz; } /* (non-Javadoc) * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean) * * Search path for class: * Parent classloader * This contribution * Imported contributions * */ @Override protected synchronized Class loadClass(String className, boolean resolveClass) throws ClassNotFoundException { Class clazz = null; try { if (this.getParent() != null) clazz = this.getParent().loadClass(className); } catch (ClassNotFoundException e) { } if (clazz == null) clazz = findClass(className); if (resolveClass) this.resolveClass(clazz); return clazz; } /* * (non-Javadoc) * * @see java.net.URLClassLoader#findResource(java.lang.String) */ @Override public URL findResource(String name) { URL url = findResourceFromContribution(name); if (url == null) { for (Import import_ : this.contribution.getImports()) { if (matchesImport(name, import_, false)) { // Delegate the resolution to the imported contribution for (Contribution exportingContribution : import_.getExportContributions()) { if (exportingContribution.getClassLoader() instanceof ContributionClassLoader) { for (Export export : exportingContribution.getExports()) { if (import_.match(export)) { url = ((ContributionClassLoader)exportingContribution.getClassLoader()).findResourceFromContribution(name); if (url != null) break; } } if (url != null) break; } } if (url != null) break; } } } return url; } /* (non-Javadoc) * @see java.net.URLClassLoader#findResources(java.lang.String) */ @Override public Enumeration findResources(String name) throws IOException { return Collections.enumeration(findResourceSet(name)); } /* (non-Javadoc) * @see java.lang.ClassLoader#getResource(java.lang.String) * * Find a resource. * Search path for resource: * Parent classloader * This contribution * Imported contributions */ @Override public URL getResource(String resName) { URL resource = null; if (this.getParent() != null) { resource = this.getParent().getResource(resName); } if (resource == null) resource = findResource(resName); return resource; } /* (non-Javadoc) * @see java.lang.ClassLoader#getResources(java.lang.String) * * Return list of resources from this contribution, resources * imported through imported contributions and resources from parent * classloader. */ @Override public Enumeration getResources(String resName) throws IOException { HashSet resourceSet = findResourceSet(resName); addEnumerationToCollection(resourceSet, super.getResources(resName)); return Collections.enumeration(resourceSet); } /* * Find set of resources */ private HashSet findResourceSet(String name) throws IOException { HashSet resources = new HashSet(); addEnumerationToCollection(resources, super.findResources(name)); for (Import import_ : this.contribution.getImports()) { if (matchesImport(name, import_, false)) { // Delegate the resolution to the imported contribution for (Contribution exportingContribution : import_.getExportContributions()) { if (exportingContribution.getClassLoader() instanceof ContributionClassLoader) { for (Export export : exportingContribution.getExports()) { if (import_.match(export)) { addEnumerationToCollection(resources, ((ContributionClassLoader)exportingContribution.getClassLoader()).findResources(name)); } } } } } } return resources; } /* * Find class from contribution. If class has already been loaded, return loaded class. */ private Class findClassFromContribution(String className) throws ClassNotFoundException { Class clazz = findLoadedClass(className); if (clazz == null) clazz = super.findClass(className); return clazz; } /* * Find resource from contribution. */ private URL findResourceFromContribution(String name) { return super.findResource(name); } /** * Check if a class or resource matches an import statement. * Class matches if the package name used in matches * Resource matches if package/namespace match the directory of resource. * * @param name Name of class or resource * @param import_ SCA contribution import * @param matchJavaClass * @return true if this is a matching import */ private boolean matchesImport(String name, Import import_, boolean matchJavaClass) { if (matchJavaClass) { if (import_ instanceof JavaImport && name != null && name.lastIndexOf('.') > 0) { JavaImport javaImport = (JavaImport) import_; String packageName = name.substring(0, name.lastIndexOf('.')); if (javaImport.getPackage() == null) return false; else return packageName.equals(javaImport.getPackage()); } } else { if (name == null || name.lastIndexOf('/') <= 0) return false; else if (import_ instanceof JavaImport) { JavaImport javaImport = (JavaImport) import_; String packageName = name.substring(0, name.lastIndexOf('/')); if (javaImport.getPackage() == null) return false; else return packageName.equals(javaImport.getPackage().replaceAll("\\.", "/")); } else if (import_ instanceof NamespaceImport) { NamespaceImport namespaceImport = (NamespaceImport) import_; String namespace = name.substring(0, name.lastIndexOf('/')); if (namespaceImport.getNamespace() == null) return false; else return namespaceImport.getNamespace().equals(namespace); } } return false; } /* * Add an enumeration to a Collection */ private void addEnumerationToCollection(Collection collection, Enumeration enumeration) { while (enumeration.hasMoreElements()) collection.add(enumeration.nextElement()); } @Override public String toString() { return "SCA contribution classloader for : " + contribution.getLocation(); } }