Provides utility classes to create the classloader used by the manipulator to compute frames, and add a classloader parameter to the pojoization methods. (FELIX-4509).
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1592768 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
index c01fb81..7e72309 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/ManipulationEngine.java
@@ -34,6 +34,10 @@
public class ManipulationEngine {
/**
+ * The classloader given to the manipulator to load classes.
+ */
+ private final ClassLoader m_classLoader;
+ /**
* List of component types.
*/
private List<ManipulationUnit> m_manipulationUnits = new ArrayList<ManipulationUnit>();
@@ -53,6 +57,10 @@
*/
private ManipulationVisitor m_manipulationVisitor;
+ public ManipulationEngine(ClassLoader classLoader) {
+ m_classLoader = classLoader;
+ }
+
/**
* Add information related to a discovered component that will be manipulated.
* @param component additional component
@@ -106,7 +114,7 @@
// Should always be the case
// Manipulation preparation
- Manipulator manipulator = new Manipulator();
+ Manipulator manipulator = new Manipulator(m_classLoader);
try {
manipulator.prepare(bytecode);
} catch (IOException e) {
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
index 6239b4c..b9b0ce6 100644
--- a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
@@ -19,22 +19,11 @@
package org.apache.felix.ipojo.manipulator;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.jar.JarFile;
-
import org.apache.felix.ipojo.manipulator.manifest.FileManifestProvider;
-import org.apache.felix.ipojo.manipulator.metadata.AnnotationMetadataProvider;
-import org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProvider;
-import org.apache.felix.ipojo.manipulator.metadata.EmptyMetadataProvider;
-import org.apache.felix.ipojo.manipulator.metadata.FileMetadataProvider;
-import org.apache.felix.ipojo.manipulator.metadata.StreamMetadataProvider;
+import org.apache.felix.ipojo.manipulator.metadata.*;
import org.apache.felix.ipojo.manipulator.render.MetadataRenderer;
import org.apache.felix.ipojo.manipulator.reporter.SystemReporter;
import org.apache.felix.ipojo.manipulator.spi.ModuleProvider;
-import org.apache.felix.ipojo.manipulator.spi.provider.CoreModuleProvider;
import org.apache.felix.ipojo.manipulator.spi.provider.ServiceLoaderModuleProvider;
import org.apache.felix.ipojo.manipulator.store.DirectoryResourceStore;
import org.apache.felix.ipojo.manipulator.store.JarFileResourceStore;
@@ -48,8 +37,15 @@
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.xml.parser.SchemaResolver;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.jar.JarFile;
+
/**
* Pojoization allows creating an iPOJO bundle from a "normal" bundle.
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class Pojoization {
@@ -123,16 +119,34 @@
return m_reporter.getWarnings();
}
+ /**
+ * Manipulates an input bundle.
+ * This method creates an iPOJO bundle based on the given metadata file.
+ * The original and final bundles must be different.
+ * <p/>
+ * This method does not use the classloader parameter, and use the classloader having loaded the
+ * {@link org.apache.felix.ipojo.manipulator.Pojoization} class. It's definitely not recommended.
+ *
+ * @param in the original bundle.
+ * @param out the final bundle.
+ * @param metadata the iPOJO metadata input stream.
+ * @deprecated
+ */
+ public void pojoization(File in, File out, InputStream metadata) {
+ pojoization(in, out, metadata, this.getClass().getClassLoader());
+ }
/**
* Manipulates an input bundle.
* This method creates an iPOJO bundle based on the given metadata file.
* The original and final bundles must be different.
- * @param in the original bundle.
- * @param out the final bundle.
+ *
+ * @param in the original bundle.
+ * @param out the final bundle.
* @param metadata the iPOJO metadata input stream.
+ * @param loader the classloader used to compute the bytecode frames.
*/
- public void pojoization(File in, File out, InputStream metadata) {
+ public void pojoization(File in, File out, InputStream metadata, ClassLoader loader) {
StreamMetadataProvider provider = new StreamMetadataProvider(metadata, m_reporter);
provider.setValidateUsingLocalSchemas(m_useLocalXSD);
@@ -156,7 +170,7 @@
ManipulationVisitor visitor = createDefaultVisitorChain(store);
- pojoization(store, provider, visitor);
+ pojoization(store, provider, visitor, loader);
}
private ManipulationVisitor createDefaultVisitorChain(ResourceStore store) {
@@ -173,11 +187,30 @@
* Manipulates an input bundle.
* This method creates an iPOJO bundle based on the given metadata file.
* The original and final bundles must be different.
- * @param in the original bundle.
- * @param out the final bundle.
+ * <p/>
+ * This method does not use the classloader parameter, and use the classloader having loaded the
+ * {@link org.apache.felix.ipojo.manipulator.Pojoization} class. It's definitely not recommended.
+ *
+ * @param in the original bundle.
+ * @param out the final bundle.
* @param metadataFile the iPOJO metadata file (XML).
+ * @deprecated
*/
public void pojoization(File in, File out, File metadataFile) {
+ pojoization(in, out, metadataFile, this.getClass().getClassLoader());
+ }
+
+ /**
+ * Manipulates an input bundle.
+ * This method creates an iPOJO bundle based on the given metadata file.
+ * The original and final bundles must be different.
+ *
+ * @param in the original bundle.
+ * @param out the final bundle.
+ * @param metadataFile the iPOJO metadata file (XML).
+ * @param loader the classloader used to compute the bytecode frames.
+ */
+ public void pojoization(File in, File out, File metadataFile, ClassLoader loader) {
MetadataProvider provider = new EmptyMetadataProvider();
if (metadataFile != null) {
FileMetadataProvider fileMetadataProvider = new FileMetadataProvider(metadataFile, m_reporter);
@@ -205,18 +238,37 @@
ManipulationVisitor visitor = createDefaultVisitorChain(store);
- pojoization(store, provider, visitor);
+ pojoization(store, provider, visitor, loader);
}
/**
* Manipulates an expanded bundles.
* Classes are in the specified directory.
* this method allows to update a customized manifest.
- * @param directory the directory containing classes
+ * <p/>
+ * This method does not use the classloader parameter, and use the classloader having loaded the
+ * {@link org.apache.felix.ipojo.manipulator.Pojoization} class. It's definitely not recommended.
+ *
+ * @param directory the directory containing classes
* @param metadataFile the metadata file
* @param manifestFile the manifest file. <code>null</code> to use directory/META-INF/MANIFEST.mf
+ * @deprecated
*/
public void directoryPojoization(File directory, File metadataFile, File manifestFile) {
+ directoryPojoization(directory, metadataFile, manifestFile, this.getClass().getClassLoader());
+ }
+
+ /**
+ * Manipulates an expanded bundles.
+ * Classes are in the specified directory.
+ * this method allows to update a customized manifest.
+ *
+ * @param directory the directory containing classes
+ * @param metadataFile the metadata file
+ * @param manifestFile the manifest file. <code>null</code> to use directory/META-INF/MANIFEST.mf
+ * @param loader the classloader used to compute the bytecode frames.
+ */
+ public void directoryPojoization(File directory, File metadataFile, File manifestFile, ClassLoader loader) {
// Get the metadata.xml location if not null
MetadataProvider provider = new EmptyMetadataProvider();
if (metadataFile != null) {
@@ -278,15 +330,15 @@
ManipulationVisitor visitor = createDefaultVisitorChain(store);
- pojoization(store, provider, visitor);
+ pojoization(store, provider, visitor, loader);
}
public void pojoization(final ResourceStore store,
final MetadataProvider metadata,
- final ManipulationVisitor visitor) {
+ final ManipulationVisitor visitor, ClassLoader loader) {
- ManipulationEngine engine = new ManipulationEngine();
+ ManipulationEngine engine = new ManipulationEngine(loader);
engine.setResourceStore(store);
engine.setReporter(m_reporter);
engine.setManipulationVisitor(visitor);
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/Classpath.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/Classpath.java
new file mode 100644
index 0000000..ac5c3f1
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/Classpath.java
@@ -0,0 +1,180 @@
+/*
+ * 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.felix.ipojo.manipulator.util;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * An ordered list of classpath elements with set behaviour. A Classpath is immutable and thread safe.
+ * This class let retrieve an isolated classloader to load classes from a set of jars.
+ */
+public class Classpath implements Iterable<String> {
+
+ private final List<String> unmodifiableElements;
+
+ public static Classpath join(Classpath firstClasspath, Classpath secondClasspath) {
+ LinkedHashSet<String> accumulated = new LinkedHashSet<String>();
+ if (firstClasspath != null) firstClasspath.addTo(accumulated);
+ if (secondClasspath != null) secondClasspath.addTo(accumulated);
+ return new Classpath(accumulated);
+ }
+
+
+ private void addTo(Collection<String> c) {
+ c.addAll(unmodifiableElements);
+ }
+
+ private Classpath() {
+ this.unmodifiableElements = Collections.emptyList();
+ }
+
+
+ public Classpath(Classpath other, String additionalElement) {
+ ArrayList<String> elems = new ArrayList<String>(other.unmodifiableElements);
+ elems.add(additionalElement);
+ this.unmodifiableElements = Collections.unmodifiableList(elems);
+ }
+
+ public Classpath(Iterable<String> paths) {
+ List<String> newCp = new ArrayList<String>();
+ for (String element : paths) {
+ newCp.add(element);
+ }
+ this.unmodifiableElements = Collections.unmodifiableList(newCp);
+ }
+
+ public static Classpath emptyClasspath() {
+ return new Classpath();
+ }
+
+ public Classpath addClassPathElementUrl(String path) {
+ if (path == null) {
+ throw new IllegalArgumentException("Null is not a valid class path element url.");
+ }
+ return !unmodifiableElements.contains(path) ? new Classpath(this, path) : this;
+ }
+
+ public List<String> getClassPath() {
+ return unmodifiableElements;
+ }
+
+ public List<URL> getAsUrlList()
+ throws MalformedURLException {
+ List<URL> urls = new ArrayList<URL>();
+ for (String path : unmodifiableElements) {
+ File f = new File(path);
+ urls.add(f.toURI().toURL());
+ }
+ return urls;
+ }
+
+ public void writeToSystemProperty(String propertyName) {
+ StringBuilder sb = new StringBuilder();
+ for (String element : unmodifiableElements) {
+ sb.append(element).append(File.pathSeparatorChar);
+ }
+ System.setProperty(propertyName, sb.toString());
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Classpath classpath = (Classpath) o;
+
+ return !(unmodifiableElements
+ != null ? !unmodifiableElements.equals(classpath.unmodifiableElements) : classpath.unmodifiableElements != null);
+
+ }
+
+ public ClassLoader createClassLoader(ClassLoader parent, boolean childDelegation)
+ throws RuntimeException {
+ try {
+ List urls = getAsUrlList();
+ IsolatedClassLoader classLoader = new IsolatedClassLoader(parent, childDelegation);
+ for (Object url1 : urls) {
+ URL url = (URL) url1;
+ classLoader.addURL(url);
+ }
+ return classLoader;
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("When creating classloader", e);
+ }
+ }
+
+ public ClassLoader createClassLoader()
+ throws RuntimeException {
+ try {
+ List urls = getAsUrlList();
+ IsolatedClassLoader classLoader = new IsolatedClassLoader();
+ for (Object url1 : urls) {
+ URL url = (URL) url1;
+ classLoader.addURL(url);
+ }
+ return classLoader;
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("When creating classloader", e);
+ }
+ }
+
+
+ public int hashCode() {
+ return unmodifiableElements != null ? unmodifiableElements.hashCode() : 0;
+ }
+
+ public String getLogMessage(String descriptor) {
+ StringBuilder result = new StringBuilder();
+ result.append(descriptor).append(" classpath:");
+ for (String element : unmodifiableElements) {
+ result.append(" ").append(element);
+ }
+ return result.toString();
+ }
+
+ public String getCompactLogMessage(String descriptor) {
+ StringBuilder result = new StringBuilder();
+ result.append(descriptor).append(" classpath:");
+ for (String element : unmodifiableElements) {
+ result.append(" ");
+ if (element != null) {
+ int pos = element.lastIndexOf(File.separatorChar);
+ if (pos >= 0) {
+ result.append(element.substring(pos + 1));
+ } else {
+ result.append(element);
+ }
+
+ } else {
+ result.append(element);
+ }
+ }
+ return result.toString();
+ }
+
+ public Iterator<String> iterator() {
+ return unmodifiableElements.iterator();
+ }
+}
diff --git a/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/IsolatedClassLoader.java b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/IsolatedClassLoader.java
new file mode 100644
index 0000000..f5a35f1
--- /dev/null
+++ b/ipojo/manipulator/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/util/IsolatedClassLoader.java
@@ -0,0 +1,85 @@
+/*
+ * 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.felix.ipojo.manipulator.util;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A classloader used by the {@link org.apache.felix.ipojo.manipulator.util.Classpath}.
+ */
+public class IsolatedClassLoader
+ extends URLClassLoader {
+ private final ClassLoader parent = ClassLoader.getSystemClassLoader();
+
+ private final Set<URL> urls = new HashSet<URL>();
+
+ private boolean childDelegation = true;
+
+ private static final URL[] EMPTY_URL_ARRAY = new URL[0];
+
+ public IsolatedClassLoader(ClassLoader parent, boolean childDelegation) {
+ super(EMPTY_URL_ARRAY, parent);
+ this.childDelegation = childDelegation;
+ }
+
+ public IsolatedClassLoader() {
+ super(EMPTY_URL_ARRAY, ClassLoader.getSystemClassLoader());
+ this.childDelegation = true;
+ }
+
+ public void addURL(URL url) {
+ // avoid duplicates
+ if (!urls.contains(url)) {
+ super.addURL(url);
+ urls.add(url);
+ }
+ }
+
+ public synchronized Class loadClass(String name)
+ throws ClassNotFoundException {
+ Class c;
+ if (childDelegation) {
+ c = findLoadedClass(name);
+
+ ClassNotFoundException ex = null;
+
+ if (c == null) {
+ try {
+ c = findClass(name);
+ } catch (ClassNotFoundException e) {
+ ex = e;
+ if (parent != null) {
+ c = parent.loadClass(name);
+ }
+ }
+ }
+
+ if (c == null) {
+ throw ex;
+ }
+ } else {
+ c = super.loadClass(name);
+ }
+
+ return c;
+ }
+}