You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2016/11/10 14:19:08 UTC
svn commit: r1769127 - in /sling/trunk/bundles/commons/classloader/src:
main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java
test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java
Author: radu
Date: Thu Nov 10 14:19:08 2016
New Revision: 1769127
URL: http://svn.apache.org/viewvc?rev=1769127&view=rev
Log:
SLING-6258 - The PackageAdminClassLoader cannot load classes from bundles providing older API versions
* made sure to lock-down the bundle which provides first a resource / class from a requested package so
that subsequent calls for resolving classes / resources from the same package will always use the same
bundle
Modified:
sling/trunk/bundles/commons/classloader/src/main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java
sling/trunk/bundles/commons/classloader/src/test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java
Modified: sling/trunk/bundles/commons/classloader/src/main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/classloader/src/main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java?rev=1769127&r1=1769126&r2=1769127&view=diff
==============================================================================
--- sling/trunk/bundles/commons/classloader/src/main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java (original)
+++ sling/trunk/bundles/commons/classloader/src/main/java/org/apache/sling/commons/classloader/impl/PackageAdminClassLoader.java Thu Nov 10 14:19:08 2016
@@ -23,6 +23,7 @@ import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,6 +32,8 @@ import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* The <code>PackageAdminClassLoader</code> loads
@@ -38,6 +41,8 @@ import org.osgi.service.packageadmin.Pac
*/
class PackageAdminClassLoader extends ClassLoader {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PackageAdminClassLoader.class);
+
/** The package admin service. */
private final PackageAdmin packageAdmin;
@@ -50,6 +55,8 @@ class PackageAdminClassLoader extends Cl
/** Negative class cache. */
private Set<String> negativeClassCache = Collections.synchronizedSet(new HashSet<String>());
+ private Map<String, Bundle> packageProviders = new ConcurrentHashMap<>();
+
/** A cache for resolved urls. */
private Map<String, URL> urlCache = new ConcurrentHashMap<String, URL>();
@@ -100,7 +107,7 @@ class PackageAdminClassLoader extends Cl
*/
private Set<Bundle> findBundlesForPackage(final String pckName) {
final ExportedPackage[] exportedPackages = this.packageAdmin.getExportedPackages(pckName);
- Set<Bundle> bundles = new HashSet<>();
+ Set<Bundle> bundles = new LinkedHashSet<>();
if (exportedPackages != null) {
for (ExportedPackage exportedPackage : exportedPackages) {
if (!exportedPackage.isRemovalPending()) {
@@ -143,10 +150,23 @@ class PackageAdminClassLoader extends Cl
public Enumeration<URL> getResources(final String name) throws IOException {
Enumeration<URL> e = super.getResources(name);
if ( e == null || !e.hasMoreElements() ) {
- for (Bundle bundle : findBundlesForPackage(getPackageFromResource(name))) {
- e = bundle.getResources(name);
- if (e != null) {
- return e;
+ String packageName = getPackageFromResource(name);
+ Bundle providingBundle = packageProviders.get(packageName);
+ if (providingBundle == null) {
+ for (Bundle bundle : findBundlesForPackage(getPackageFromResource(name))) {
+ e = bundle.getResources(name);
+ if (e != null) {
+ packageProviders.put(packageName, bundle);
+ LOGGER.debug("Marking bundle {}:{} as the provider for API package {}.", bundle.getSymbolicName(), bundle
+ .getVersion().toString(), packageName);
+ return e;
+ }
+ }
+ } else {
+ e = providingBundle.getResources(name);
+ if (e == null) {
+ LOGGER.debug("Cannot find resources {} in bundle {}:{} which was marked as the provider for package {}.", name,
+ providingBundle.getSymbolicName(), providingBundle.getVersion().toString(), packageName);
}
}
}
@@ -163,12 +183,25 @@ class PackageAdminClassLoader extends Cl
}
URL url = super.findResource(name);
if ( url == null ) {
- Set<Bundle> bundles = findBundlesForPackage(getPackageFromResource(name));
- for (Bundle bundle : bundles) {
- url = bundle.getResource(name);
- if ( url != null ) {
- urlCache.put(name, url);
- return url;
+ String packageName = getPackageFromResource(name);
+ Bundle providingBundle = packageProviders.get(packageName);
+ if (providingBundle == null) {
+ Set<Bundle> bundles = findBundlesForPackage(getPackageFromResource(name));
+ for (Bundle bundle : bundles) {
+ url = bundle.getResource(name);
+ if (url != null) {
+ urlCache.put(name, url);
+ packageProviders.put(packageName, bundle);
+ LOGGER.debug("Marking bundle {}:{} as the provider for API package {}.", bundle.getSymbolicName(), bundle
+ .getVersion().toString(), packageName);
+ return url;
+ }
+ }
+ } else {
+ url = providingBundle.getResource(name);
+ if (url == null) {
+ LOGGER.debug("Cannot find resource {} in bundle {}:{} which was marked as the provider for package {}.", name,
+ providingBundle.getSymbolicName(), providingBundle.getVersion().toString(), packageName);
}
}
}
@@ -183,19 +216,14 @@ class PackageAdminClassLoader extends Cl
if ( cachedClass != null ) {
return cachedClass;
}
- Class<?> clazz = null;
+ Class<?> clazz;
try {
clazz = super.findClass(name);
} catch (ClassNotFoundException cnfe) {
- Set<Bundle> bundles = findBundlesForPackage(getPackageFromClassName(name));
- for (Bundle bundle : bundles) {
- try {
- clazz = bundle.loadClass(name);
- this.factory.addUsedBundle(bundle);
- break;
- } catch (ClassNotFoundException icnfe) {
- // do nothing; we need to loop over the bundles providing the class' package
- }
+ try {
+ clazz = getClassFromBundles(name);
+ } catch (ClassNotFoundException innerCNFE) {
+ throw innerCNFE;
}
}
if ( clazz == null ) {
@@ -216,29 +244,54 @@ class PackageAdminClassLoader extends Cl
if ( negativeClassCache.contains(name) ) {
throw new ClassNotFoundException("Class not found " + name);
}
- Class<?> clazz = null;
+ String packageName = getPackageFromClassName(name);
+ Class<?> clazz;
try {
clazz = super.loadClass(name, resolve);
} catch (final ClassNotFoundException cnfe) {
- final String pckName = getPackageFromClassName(name);
- Set<Bundle> bundles = findBundlesForPackage(pckName);
+ try {
+ clazz = getClassFromBundles(name);
+ } catch (ClassNotFoundException innerCNFE) {
+ negativeClassCache.add(name);
+ this.factory.addUnresolvedPackage(packageName);
+ throw innerCNFE;
+ }
+ }
+ if ( clazz == null ) {
+ negativeClassCache.add(name);
+ this.factory.addUnresolvedPackage(packageName);
+ throw new ClassNotFoundException("Class not found " + name);
+ }
+ this.classCache.put(name, clazz);
+ return clazz;
+ }
+
+ private Class<?> getClassFromBundles(String name) throws ClassNotFoundException {
+ Class<?> clazz = null;
+ String packageName = getPackageFromClassName(name);
+ Bundle providingBundle = packageProviders.get(packageName);
+ if (providingBundle == null) {
+ Set<Bundle> bundles = findBundlesForPackage(packageName);
for (Bundle bundle : bundles) {
try {
clazz = bundle.loadClass(name);
this.factory.addUsedBundle(bundle);
+ packageProviders.put(packageName, bundle);
+ LOGGER.debug("Marking bundle {}:{} as the provider for API package {}.", bundle.getSymbolicName(), bundle
+ .getVersion().toString(), packageName);
break;
- } catch (final ClassNotFoundException inner) {
+ } catch (ClassNotFoundException innerCNFE) {
// do nothing; we need to loop over the bundles providing the class' package
}
}
+ } else {
+ try {
+ clazz = providingBundle.loadClass(name);
+ } catch (ClassNotFoundException icnfe) {
+ throw new ClassNotFoundException(String.format("Cannot find class %s in bundle %s:%s which was marked as the provider for" +
+ " package %s.", name, providingBundle.getSymbolicName(), providingBundle.getVersion().toString(), packageName), icnfe);
+ }
}
- if ( clazz == null ) {
- negativeClassCache.add(name);
- final String pckName = getPackageFromClassName(name);
- this.factory.addUnresolvedPackage(pckName);
- throw new ClassNotFoundException("Class not found " + name);
- }
- this.classCache.put(name, clazz);
return clazz;
}
}
Modified: sling/trunk/bundles/commons/classloader/src/test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/classloader/src/test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java?rev=1769127&r1=1769126&r2=1769127&view=diff
==============================================================================
--- sling/trunk/bundles/commons/classloader/src/test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java (original)
+++ sling/trunk/bundles/commons/classloader/src/test/java/org/apache/sling/commons/classloader/impl/ClassLoadingTest.java Thu Nov 10 14:19:08 2016
@@ -16,7 +16,11 @@
*/
package org.apache.sling.commons.classloader.impl;
+import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
import org.jmock.Expectations;
import org.jmock.Mockery;
@@ -27,6 +31,7 @@ import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceListener;
+import org.osgi.framework.Version;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
@@ -68,6 +73,10 @@ public class ClassLoadingTest {
will(returnValue(2L));
allowing(bundle).getState();
will(returnValue(Bundle.ACTIVE));
+ allowing(bundle).getSymbolicName();
+ will(returnValue("org.apache.sling.test"));
+ allowing(bundle).getVersion();
+ will(returnValue(new Version("1.0.0")));
one(bundle).loadClass("org.apache.sling.test.A"); inSequence(sequence);
will(returnValue(java.util.Map.class));
one(bundle).loadClass("org.apache.sling.test.A"); inSequence(sequence);
@@ -92,8 +101,10 @@ public class ClassLoadingTest {
final PackageAdmin packageAdmin = this.context.mock(PackageAdmin.class);
final ExportedPackage ep1 = this.context.mock(ExportedPackage.class, "ep1");
final ExportedPackage ep2 = this.context.mock(ExportedPackage.class, "ep2");
+ final ExportedPackage ep3 = this.context.mock(ExportedPackage.class, "ep3");
final Bundle bundle1 = this.context.mock(Bundle.class, "bundle1");
final Bundle bundle2 = this.context.mock(Bundle.class, "bundle2");
+ final Bundle bundle3 = this.context.mock(Bundle.class, "bundle3");
final ClassNotFoundException cnfe = new ClassNotFoundException();
this.context.checking(new Expectations() {{
allowing(bundleContext).createFilter(with(any(String.class)));
@@ -102,8 +113,10 @@ public class ClassLoadingTest {
will(returnValue(null));
allowing(bundleContext).addServiceListener(with(any(ServiceListener.class)), with(any(String.class)));
allowing(bundleContext).removeServiceListener(with(any(ServiceListener.class)));
- allowing(packageAdmin).getExportedPackages("org.apache.sling.test");
+ allowing(packageAdmin).getExportedPackages(with("org.apache.sling.test"));
will(returnValue(new ExportedPackage[] {ep1, ep2}));
+ allowing(packageAdmin).getExportedPackages(with("org.apache.sling.test3"));
+ will(returnValue(new ExportedPackage[] {ep3}));
allowing(ep1).getExportingBundle();
will(returnValue(bundle1));
@@ -115,27 +128,81 @@ public class ClassLoadingTest {
allowing(ep2).isRemovalPending();
will(returnValue(false));
+ allowing(ep3).getExportingBundle();
+ will(returnValue(bundle3));
+ allowing(ep3).isRemovalPending();
+ will(returnValue(false));
+
allowing(bundle1).getBundleId();
- will(returnValue(2L));
+ will(returnValue(1L));
allowing(bundle1).getState();
will(returnValue(Bundle.ACTIVE));
+ allowing(bundle1).getVersion();
+ will(returnValue(new Version("1.0.0")));
+ allowing(bundle1).getSymbolicName();
+ will(returnValue("org.apache.sling.test1"));
allowing(bundle2).getBundleId();
- will(returnValue(3L));
+ will(returnValue(2L));
allowing(bundle2).getState();
will(returnValue(Bundle.ACTIVE));
+ allowing(bundle2).getVersion();
+ will(returnValue(new Version("2.0.0")));
+ allowing(bundle2).getSymbolicName();
+ will(returnValue("org.apache.sling.test2"));
+
+ allowing(bundle3).getBundleId();
+ will(returnValue(3L));
+ allowing(bundle3).getState();
+ will(returnValue(Bundle.ACTIVE));
+ allowing(bundle3).getVersion();
+ will(returnValue(new Version("1.2.3")));
+ allowing(bundle3).getSymbolicName();
+ will(returnValue("org.apache.sling.test3"));
allowing(bundle1).loadClass("org.apache.sling.test.T1");
will(throwException(cnfe));
+ allowing(bundle1).getResource("org/apache/sling/test/T3.class");
+ will(returnValue(new URL("jar:file:/ws/org.apache.sling.test1.jar!/org/apache/sling/test/T3.class")));
allowing(bundle2).loadClass("org.apache.sling.test.T1");
will(returnValue(ArrayList.class));
+ allowing(bundle2).loadClass("org.apache.sling.test.T2");
+ will(throwException(new ClassNotFoundException()));
+ allowing(bundle2).getResource("org/apache/sling/test/T3.class");
+ will(returnValue(null));
+ allowing(bundle2).getResources("org/apache/sling/test/T3.class");
+ will(returnValue(null));
+ allowing(bundle3).getResource("org/apache/sling/test3/T4.class");
+ will(returnValue(new URL("jar:file:/ws/org.apache.sling.test3.jar!/org/apache/sling/test3/T4.class")));
+ allowing(bundle3).getResources("org/apache/sling/test3/T4.class");
+ will(returnValue(Collections.enumeration(new ArrayList<URL>() {{
+ add(new URL("jar:file:/ws/org.apache.sling.test3.jar!/org/apache/sling/test3/T4.class"));
+ }})));
}});
DynamicClassLoaderManagerImpl manager = new DynamicClassLoaderManagerImpl(bundleContext, packageAdmin, null,
new DynamicClassLoaderManagerFactory(bundleContext, packageAdmin));
final ClassLoader cl = manager.getDynamicClassLoader();
Assert.assertEquals(ArrayList.class, cl.loadClass("org.apache.sling.test.T1"));
+ try {
+ cl.loadClass("org.apache.sling.test.T2");
+ } catch (Exception e) {
+ Assert.assertEquals(ClassNotFoundException.class, e.getClass());
+ }
+ Assert.assertNull(cl.getResource("org/apache/sling/test/T3.class"));
+ Assert.assertFalse(cl.getResources("org/apache/sling/test/T3.class").hasMoreElements());
+ Assert.assertEquals("jar:file:/ws/org.apache.sling.test3.jar!/org/apache/sling/test3/T4.class", cl.getResource
+ ("org/apache/sling/test3/T4.class").toString());
+ Enumeration<URL> resourceURLs = cl.getResources("org/apache/sling/test3/T4.class");
+ int count = 0;
+ URL lastURL = new URL("https://sling.apache.org");
+ while (resourceURLs.hasMoreElements()) {
+ count++;
+ lastURL = resourceURLs.nextElement();
+ }
+ Assert.assertEquals(1, count);
+ Assert.assertEquals("jar:file:/ws/org.apache.sling.test3.jar!/org/apache/sling/test3/T4.class", lastURL.toString());
}
}