You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2018/04/13 21:35:55 UTC
svn commit: r1829110 - in /felix/trunk/osgi-r7/framework/src:
main/java/org/apache/felix/framework/ test/java/org/apache/felix/framework/
Author: pauls
Date: Fri Apr 13 21:35:54 2018
New Revision: 1829110
URL: http://svn.apache.org/viewvc?rev=1829110&view=rev
Log:
FELIX-5827: Use less synchronized in the BundleWiring and BundleClassloader.
Modified:
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/BundleWiringImplTest.java
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java?rev=1829110&r1=1829109&r2=1829110&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java (original)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java Fri Apr 13 21:35:54 2018
@@ -71,9 +71,9 @@ public class BundleRevisionImpl implemen
private final BundleImpl m_bundle;
- private Content m_content;
- private List<Content> m_contentPath;
- private ProtectionDomain m_protectionDomain = null;
+ private volatile Content m_content;
+ private volatile List<Content> m_contentPath;
+ private volatile ProtectionDomain m_protectionDomain = null;
private final static SecureAction m_secureAction = new SecureAction();
// Bundle wiring when resolved.
@@ -341,12 +341,12 @@ public class BundleRevisionImpl implemen
}
}
- public synchronized void setProtectionDomain(ProtectionDomain pd)
+ public void setProtectionDomain(ProtectionDomain pd)
{
m_protectionDomain = pd;
}
- public synchronized ProtectionDomain getProtectionDomain()
+ public ProtectionDomain getProtectionDomain()
{
return m_protectionDomain;
}
@@ -355,17 +355,17 @@ public class BundleRevisionImpl implemen
// Content access methods.
//
- public synchronized Content getContent()
+ public Content getContent()
{
return m_content;
}
- synchronized void resetContent(Content content)
+ void resetContent(Content content)
{
m_content = content;
}
- synchronized List<Content> getContentPath()
+ List<Content> getContentPath()
{
if (m_contentPath == null)
{
@@ -382,8 +382,12 @@ public class BundleRevisionImpl implemen
return m_contentPath;
}
- private List<Content> initializeContentPath() throws Exception
+ private synchronized List<Content> initializeContentPath() throws Exception
{
+ if (m_contentPath != null)
+ {
+ return m_contentPath;
+ }
List<Content> contentList = new ArrayList();
calculateContentPath(this, getContent(), contentList, true);
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java?rev=1829110&r1=1829109&r2=1829110&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java (original)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java Fri Apr 13 21:35:54 2018
@@ -30,6 +30,7 @@ import org.apache.felix.framework.util.U
import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.apache.felix.framework.util.manifestparser.NativeLibrary;
import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleReference;
@@ -59,6 +60,7 @@ import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.SecureClassLoader;
@@ -114,7 +116,7 @@ public class BundleWiringImpl implements
private volatile List<BundleRequirement> m_wovenReqs = null;
- private BundleClassLoader m_classLoader;
+ private volatile BundleClassLoader m_classLoader;
// Bundle-specific class loader for boot delegation.
private final ClassLoader m_bootClassLoader;
@@ -538,7 +540,7 @@ public class BundleWiringImpl implements
}
@Override
- public synchronized boolean isInUse()
+ public boolean isInUse()
{
return !m_isDisposed;
}
@@ -706,32 +708,39 @@ public class BundleWiringImpl implements
{
return null;
}
+
return getClassLoaderInternal();
}
- private synchronized ClassLoader getClassLoaderInternal()
+ private ClassLoader getClassLoaderInternal()
+ {
+ ClassLoader classLoader = m_classLoader;
+ if (m_classLoader != null)
+ {
+ return classLoader;
+ }
+ else
+ {
+ return _getClassLoaderInternal();
+ }
+ }
+
+ private synchronized ClassLoader _getClassLoaderInternal()
{
// Only try to create the class loader if the bundle
// is not disposed.
if (!m_isDisposed && (m_classLoader == null))
{
- Class clazz = BundleClassLoader.class;
-
- // Use SecureAction to create the class loader if security is
- // enabled; otherwise, create it directly.
- try
- {
- Constructor ctor = BundleRevisionImpl.getSecureAction()
- .getConstructor(clazz, new Class[] { BundleWiringImpl.class, ClassLoader.class, Logger.class });
- m_classLoader = (BundleClassLoader)
- BundleRevisionImpl.getSecureAction().invoke(ctor,
- new Object[] { this, determineParentClassLoader(), m_logger });
- }
- catch (Exception ex)
- {
- throw new RuntimeException("Unable to create module class loader: "
- + ex.getMessage() + " [" + ex.getClass().getName() + "]");
- }
+ m_classLoader = BundleRevisionImpl.getSecureAction().run(
+ new PrivilegedAction<BundleClassLoader>()
+ {
+ @Override
+ public BundleClassLoader run()
+ {
+ return new BundleClassLoader(BundleWiringImpl.this, determineParentClassLoader(), m_logger);
+ }
+ }
+ );
}
return m_classLoader;
}
@@ -762,9 +771,8 @@ public class BundleWiringImpl implements
// Thread local to detect class loading cycles.
private final ThreadLocal m_listResourcesCycleCheck = new ThreadLocal();
- // TODO: OSGi R4.3 - Should this be synchronized or should we take a snapshot?
@Override
- public synchronized Collection<String> listResources(
+ public Collection<String> listResources(
String path, String filePattern, int options)
{
// Implementation note: If you enable the DEBUG option for
@@ -817,7 +825,7 @@ public class BundleWiringImpl implements
return resources;
}
- private Collection<ResourceSource> listResourcesInternal(
+ private synchronized Collection<ResourceSource> listResourcesInternal(
String path, List<String> pattern, int options)
{
if (isInUse())
@@ -1331,13 +1339,15 @@ public class BundleWiringImpl implements
return result;
}
- synchronized ClassLoader getBootDelegationClassLoader()
+ ClassLoader getBootDelegationClassLoader()
{
+ ClassLoader loader = m_classLoader;
// Get the appropriate class loader for delegation.
- ClassLoader parent = (m_classLoader == null)
- ? determineParentClassLoader() :
- BundleRevisionImpl.getSecureAction().getParentClassLoader(m_classLoader);
- return (parent == null) ? m_bootClassLoader : parent;
+ ClassLoader parent = (loader == null) ?
+ determineParentClassLoader() :
+ BundleRevisionImpl.getSecureAction().getParentClassLoader(loader);
+
+ return (parent == null) ? m_bootClassLoader : parent;
}
private static final Constructor m_dexFileClassConstructor;
@@ -1962,35 +1972,22 @@ public class BundleWiringImpl implements
static
{
- boolean registered = false;
- try
- {
- Method method = BundleRevisionImpl.getSecureAction()
- .getDeclaredMethod(ClassLoader.class, "registerAsParallelCapable", null);
-
- BundleRevisionImpl.getSecureAction().setAccesssible(method);
-
- registered = ((Boolean) method.invoke(null)).booleanValue();
- }
- catch (Throwable th)
- {
- // This is OK on older java versions
- }
+ m_isParallel = registerAsParallel();
+ }
- m_isParallel = registered;
+ @IgnoreJRERequirement
+ private static boolean registerAsParallel()
+ {
+ boolean registered = false;
try
{
- Method method = BundleRevisionImpl.getSecureAction()
- .getDeclaredMethod(ClassLoader.class, "registerAsParallelCapable", null);
-
- BundleRevisionImpl.getSecureAction().setAccesssible(method);
-
- method.invoke(null);
+ registered = ClassLoader.registerAsParallelCapable();
}
catch (Throwable th)
{
// This is OK on older java versions
}
+ return registered;
}
// Flag used to determine if a class has been loaded from this class
@@ -2001,7 +1998,7 @@ public class BundleWiringImpl implements
private Object[][] m_cachedLibs = new Object[0][];
private static final int LIBNAME_IDX = 0;
private static final int LIBPATH_IDX = 1;
- private final Map<String, Thread> m_classLocks = new HashMap<String, Thread>();
+ private final ConcurrentHashMap<String, Thread> m_classLocks = new ConcurrentHashMap<String, Thread>();
private final BundleWiringImpl m_wiring;
private final Logger m_logger;
@@ -2035,14 +2032,7 @@ public class BundleWiringImpl implements
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
- Class clazz;
-
- // Make sure the class was not already loaded.
- Object lock = (isParallel()) ? m_classLocks : this;
- synchronized (lock)
- {
- clazz = findLoadedClass(name);
- }
+ Class clazz = findLoadedClass(name);
if (clazz == null)
{
@@ -2085,26 +2075,26 @@ public class BundleWiringImpl implements
@Override
protected Class findClass(String name) throws ClassNotFoundException
{
- Class clazz = null;
-
- // Do a quick check to try to avoid searching for classes on a
- // disposed class loader, which will avoid some odd exception.
- // This won't prevent all weird exception, since the wiring could
- // still get disposed of after this check, but it will prevent
- // some, perhaps.
- if (m_wiring.m_isDisposed)
- {
- throw new ClassNotFoundException(
- "Unable to load class '"
- + name
- + "' because the bundle wiring for "
- + m_wiring.m_revision.getSymbolicName()
- + " is no longer valid.");
- }
+ Class clazz = findLoadedClass(name);
// Search for class in bundle revision.
if (clazz == null)
{
+ // Do a quick check to try to avoid searching for classes on a
+ // disposed class loader, which will avoid some odd exception.
+ // This won't prevent all weird exception, since the wiring could
+ // still get disposed of after this check, but it will prevent
+ // some, perhaps.
+ if (m_wiring.m_isDisposed)
+ {
+ throw new ClassNotFoundException(
+ "Unable to load class '"
+ + name
+ + "' because the bundle wiring for "
+ + m_wiring.m_revision.getSymbolicName()
+ + " is no longer valid.");
+ }
+
String actual = name.replace('.', '/') + ".class";
byte[] bytes = null;
@@ -2159,42 +2149,15 @@ public class BundleWiringImpl implements
throw e;
}
}
- // Before we actually attempt to define the class, grab
- // the lock for this class loader and make sure than no
- // other thread has defined this class in the meantime.
- Object lock = (isParallel()) ? m_classLocks : this;
- synchronized (lock)
- {
- Thread me = Thread.currentThread();
- while (m_classLocks.containsKey(name) && (m_classLocks.get(name) != me))
- {
- try
- {
- lock.wait();
- }
- catch (InterruptedException e)
- {
- // TODO: WHAT TO DO HERE?
- throw new RuntimeException(e);
- }
- }
- // Lock released, try loading class.
- clazz = findLoadedClass(name);
- if (clazz == null)
- {
- // Not found, we should try load it.
- m_classLocks.put(name, me);
- }
- }
try
{
- clazz = defineClass(felix, wovenClassListeners, wci, name,
- clazz, bytes, content, pkgName, lock);
+ clazz = isParallel() ? defineClassParallel(name, felix, wovenClassListeners, wci, bytes, content, pkgName) :
+ defineClassNotParallel(name, felix, wovenClassListeners, wci, bytes, content, pkgName);
}
catch (ClassFormatError e)
{
- if(wci != null)
+ if (wci != null)
{
wci.setState(WovenClass.DEFINE_FAILED);
callWovenClassListeners(felix, wovenClassListeners, wci);
@@ -2236,206 +2199,245 @@ public class BundleWiringImpl implements
return clazz;
}
- Class defineClass(Felix felix,
- Set<ServiceReference<WovenClassListener>> wovenClassListeners,
- WovenClassImpl wci, String name, Class clazz, byte[] bytes,
- Content content, String pkgName, Object lock)
- throws ClassFormatError
+ Class defineClassParallel(String name, Felix felix, Set<ServiceReference<WovenClassListener>> wovenClassListeners, WovenClassImpl wci, byte[] bytes,
+ Content content, String pkgName) throws ClassFormatError
{
+ Class clazz = null;
- try
+ Thread me = Thread.currentThread();
+
+ while (clazz == null && m_classLocks.putIfAbsent(name, me) != me)
{
- if (clazz == null)
+ clazz = findLoadedClass(name);
+ }
+
+ if (clazz == null)
+ {
+ try
+ {
+ clazz = findLoadedClass(name);
+ if (clazz == null)
+ {
+ clazz = defineClass(felix, wovenClassListeners, wci, name,
+ bytes, content, pkgName);
+ }
+ }
+ finally
+ {
+ m_classLocks.remove(name);
+ }
+ }
+ return clazz;
+ }
+
+ Class defineClassNotParallel(String name, Felix felix, Set<ServiceReference<WovenClassListener>> wovenClassListeners, WovenClassImpl wci, byte[] bytes,
+ Content content, String pkgName) throws ClassFormatError
+ {
+ Class clazz = findLoadedClass(name);
+
+ if (clazz == null)
+ {
+ synchronized (m_classLocks)
+ {
+ clazz = findLoadedClass(name);
+ if (clazz == null)
+ {
+ clazz = defineClass(felix, wovenClassListeners, wci, name,
+ bytes, content, pkgName);
+ }
+ }
+ }
+ return clazz;
+ }
+
+ Class defineClass(Felix felix,
+ Set<ServiceReference<WovenClassListener>> wovenClassListeners,
+ WovenClassImpl wci, String name, byte[] bytes, Content content, String pkgName)
+ throws ClassFormatError
+ {
+ // If we have a woven class then get the class bytes from
+ // it since they may have changed.
+ // NOTE: We are taking a snapshot of these values and
+ // are not preventing a malbehaving weaving hook from
+ // modifying them after the fact. The price of preventing
+ // this isn't worth it, since they can already wreck
+ // havoc via weaving anyway. However, we do pass the
+ // snapshot values into the woven class when we mark it
+ // as complete so that it will refect the actual values
+ // we used to define the class.
+ if (wci != null)
+ {
+ bytes = wci._getBytes();
+ List<String> wovenImports = wci.getDynamicImportsInternal();
+
+ // Try to add any woven dynamic imports, since they
+ // could potentially be needed when defining the class.
+ List<BundleRequirement> allWovenReqs =
+ new ArrayList<BundleRequirement>();
+ for (String s : wovenImports)
{
- // If we have a woven class then get the class bytes from
- // it since they may have changed.
- // NOTE: We are taking a snapshot of these values and
- // are not preventing a malbehaving weaving hook from
- // modifying them after the fact. The price of preventing
- // this isn't worth it, since they can already wreck
- // havoc via weaving anyway. However, we do pass the
- // snapshot values into the woven class when we mark it
- // as complete so that it will refect the actual values
- // we used to define the class.
- if (wci != null)
- {
- bytes = wci._getBytes();
- List<String> wovenImports = wci.getDynamicImportsInternal();
-
- // Try to add any woven dynamic imports, since they
- // could potentially be needed when defining the class.
- List<BundleRequirement> allWovenReqs =
- new ArrayList<BundleRequirement>();
- for (String s : wovenImports)
+ try
+ {
+ List<BundleRequirement> wovenReqs =
+ ManifestParser.parseDynamicImportHeader(
+ m_logger, m_wiring.m_revision, s);
+ allWovenReqs.addAll(wovenReqs);
+ }
+ catch (BundleException ex)
+ {
+ // There should be no exception here
+ // since we checked syntax before adding
+ // dynamic import strings to list.
+ }
+ }
+ // Add the dynamic requirements.
+ if (!allWovenReqs.isEmpty())
+ {
+ // Check for duplicate woven imports.
+ // First grab existing woven imports, if any.
+ Set<String> filters = new HashSet<String>();
+ if (m_wiring.m_wovenReqs != null)
+ {
+ for (BundleRequirement req : m_wiring.m_wovenReqs)
{
- try
- {
- List<BundleRequirement> wovenReqs =
- ManifestParser.parseDynamicImportHeader(
- m_logger, m_wiring.m_revision, s);
- allWovenReqs.addAll(wovenReqs);
- }
- catch (BundleException ex)
- {
- // There should be no exception here
- // since we checked syntax before adding
- // dynamic import strings to list.
- }
+ filters.add(
+ ((BundleRequirementImpl) req)
+ .getFilter().toString());
}
- // Add the dynamic requirements.
- if (!allWovenReqs.isEmpty())
+ }
+ // Then check new woven imports for duplicates
+ // against existing and self.
+ int idx = allWovenReqs.size();
+ while (idx < allWovenReqs.size())
+ {
+ BundleRequirement wovenReq = allWovenReqs.get(idx);
+ String filter = ((BundleRequirementImpl)
+ wovenReq).getFilter().toString();
+ if (!filters.contains(filter))
{
- // Check for duplicate woven imports.
- // First grab existing woven imports, if any.
- Set<String> filters = new HashSet<String>();
- if (m_wiring.m_wovenReqs != null)
- {
- for (BundleRequirement req : m_wiring.m_wovenReqs)
- {
- filters.add(
- ((BundleRequirementImpl) req)
- .getFilter().toString());
- }
- }
- // Then check new woven imports for duplicates
- // against existing and self.
- int idx = allWovenReqs.size();
- while (idx < allWovenReqs.size())
- {
- BundleRequirement wovenReq = allWovenReqs.get(idx);
- String filter = ((BundleRequirementImpl)
- wovenReq).getFilter().toString();
- if (!filters.contains(filter))
- {
- filters.add(filter);
- idx++;
- }
- else
- {
- allWovenReqs.remove(idx);
- }
- }
- // Merge existing with new imports, if any.
- if (!allWovenReqs.isEmpty())
- {
- if (m_wiring.m_wovenReqs != null)
- {
- allWovenReqs.addAll(0, m_wiring.m_wovenReqs);
- }
- m_wiring.m_wovenReqs = allWovenReqs;
- }
+ filters.add(filter);
+ idx++;
+ }
+ else
+ {
+ allWovenReqs.remove(idx);
}
}
+ // Merge existing with new imports, if any.
+ if (!allWovenReqs.isEmpty())
+ {
+ if (m_wiring.m_wovenReqs != null)
+ {
+ allWovenReqs.addAll(0, m_wiring.m_wovenReqs);
+ }
+ m_wiring.m_wovenReqs = allWovenReqs;
+ }
+ }
+ }
- int activationPolicy =
- getBundle().isDeclaredActivationPolicyUsed()
- ? getBundle()
- .adapt(BundleRevisionImpl.class).getDeclaredActivationPolicy()
- : EAGER_ACTIVATION;
-
- // If the revision is using deferred activation, then if
- // we load this class from this revision we need to activate
- // the bundle before returning the class. We will short
- // circuit the trigger matching if the trigger is already
- // tripped.
- boolean isTriggerClass = m_isActivationTriggered
- ? false : m_wiring.m_revision.isActivationTrigger(pkgName);
- if (!m_isActivationTriggered
- && isTriggerClass
- && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
- && (getBundle().getState() == Bundle.STARTING))
- {
- List deferredList = (List) m_deferredActivation.get();
- if (deferredList == null)
- {
- deferredList = new ArrayList();
- m_deferredActivation.set(deferredList);
- }
- deferredList.add(new Object[] { name, getBundle() });
- }
- // We need to try to define a Package object for the class
- // before we call defineClass() if we haven't already
- // created it.
- if (pkgName.length() > 0)
- {
- if (getPackage(pkgName) == null)
- {
- Object[] params = definePackage(pkgName);
-
- // This is a harmless check-then-act situation,
- // where threads might be racing to create different
- // classes in the same package, so catch and ignore
- // any IAEs that may occur.
- try
- {
- definePackage(
- pkgName,
- (String) params[0],
- (String) params[1],
- (String) params[2],
- (String) params[3],
- (String) params[4],
- (String) params[5],
- null);
- }
- catch (IllegalArgumentException ex)
- {
- // Ignore.
- }
- }
- }
+ int activationPolicy =
+ getBundle().isDeclaredActivationPolicyUsed()
+ ? getBundle()
+ .adapt(BundleRevisionImpl.class).getDeclaredActivationPolicy()
+ : EAGER_ACTIVATION;
- // If we can load the class from a dex file do so
- if (content instanceof JarContent)
- {
- try
- {
- clazz = getDexFileClass((JarContent) content, name, this);
- }
- catch (Exception ex)
- {
- // Looks like we can't
- }
- }
+ // If the revision is using deferred activation, then if
+ // we load this class from this revision we need to activate
+ // the bundle before returning the class. We will short
+ // circuit the trigger matching if the trigger is already
+ // tripped.
+ boolean isTriggerClass = m_isActivationTriggered
+ ? false : m_wiring.m_revision.isActivationTrigger(pkgName);
- if (clazz == null)
- {
- // If we have a security context, then use it to
- // define the class with it for security purposes,
- // otherwise define the class without a protection domain.
- if (m_wiring.m_revision.getProtectionDomain() != null)
- {
- clazz = defineClass(name, bytes, 0, bytes.length,
- m_wiring.m_revision.getProtectionDomain());
- }
- else
- {
- clazz = defineClass(name, bytes, 0, bytes.length);
- }
- if(wci != null)
- {
- wci.completeDefine(clazz);
- wci.setState(WovenClass.DEFINED);
- callWovenClassListeners(felix, wovenClassListeners, wci);
- }
- }
+ if (!m_isActivationTriggered
+ && isTriggerClass
+ && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
+ && (getBundle().getState() == Bundle.STARTING))
+ {
+ List deferredList = (List) m_deferredActivation.get();
+ if (deferredList == null)
+ {
+ deferredList = new ArrayList();
+ m_deferredActivation.set(deferredList);
+ }
+ deferredList.add(new Object[]{name, getBundle()});
+ }
+ // We need to try to define a Package object for the class
+ // before we call defineClass() if we haven't already
+ // created it.
+ if (pkgName.length() > 0)
+ {
+ if (getPackage(pkgName) == null)
+ {
+ Object[] params = definePackage(pkgName);
- // At this point if we have a trigger class, then the deferred
- // activation trigger has tripped.
- if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
- {
- m_isActivationTriggered = true;
- }
+ // This is a harmless check-then-act situation,
+ // where threads might be racing to create different
+ // classes in the same package, so catch and ignore
+ // any IAEs that may occur.
+ try
+ {
+ definePackage(
+ pkgName,
+ (String) params[0],
+ (String) params[1],
+ (String) params[2],
+ (String) params[3],
+ (String) params[4],
+ (String) params[5],
+ null);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ // Ignore.
+ }
}
}
- finally
+
+ Class clazz = null;
+
+ // If we can load the class from a dex file do so
+ if (content instanceof JarContent)
{
- synchronized (lock)
+ try
{
- m_classLocks.remove(name);
- lock.notifyAll();
+ clazz = getDexFileClass((JarContent) content, name, this);
+ }
+ catch (Exception ex)
+ {
+ // Looks like we can't
+ }
+ }
+
+ if (clazz == null)
+ {
+ // If we have a security context, then use it to
+ // define the class with it for security purposes,
+ // otherwise define the class without a protection domain.
+ if (m_wiring.m_revision.getProtectionDomain() != null)
+ {
+ clazz = defineClass(name, bytes, 0, bytes.length,
+ m_wiring.m_revision.getProtectionDomain());
+ }
+ else
+ {
+ clazz = defineClass(name, bytes, 0, bytes.length);
+ }
+ if (wci != null)
+ {
+ wci.completeDefine(clazz);
+ wci.setState(WovenClass.DEFINED);
+ callWovenClassListeners(felix, wovenClassListeners, wci);
}
}
+
+ // At this point if we have a trigger class, then the deferred
+ // activation trigger has tripped.
+ if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
+ {
+ m_isActivationTriggered = true;
+ }
+
return clazz;
}
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java?rev=1829110&r1=1829109&r2=1829110&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java (original)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java Fri Apr 13 21:35:54 2018
@@ -84,7 +84,7 @@ public class ResolveContextImpl extends
List<Resource> result = new ArrayList<Resource>();
for (BundleRevision revision : m_ondemand)
{
- for (BundleRequirement req : ((BundleRevisionImpl) revision).getDeclaredRequirements(BundleRevision.HOST_NAMESPACE))
+ for (BundleRequirement req : revision.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE))
{
for (Capability cap : host.getCapabilities(BundleRevision.HOST_NAMESPACE))
{
@@ -146,41 +146,27 @@ public class ResolveContextImpl extends
// already or at least could be calculated quicker taking into account the
// current state. We need to revisit this.
Set<String> exportNames = new HashSet<String>();
- for (Capability cap : wiring.getResource().getCapabilities(null))
+ for (Capability cap : wiring.getResource().getCapabilities(PackageNamespace.PACKAGE_NAMESPACE))
{
- if (PackageNamespace.PACKAGE_NAMESPACE.equals(cap.getNamespace()))
- {
- exportNames.add(
- (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
- }
+ exportNames.add(
+ (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
}
// Add fragment exports
- for (Wire wire : wiring.getProvidedResourceWires(null))
+ for (Wire wire : wiring.getProvidedResourceWires(HostNamespace.HOST_NAMESPACE))
{
- if (HostNamespace.HOST_NAMESPACE.equals(wire.getCapability().getNamespace()))
+ for (Capability cap : wire.getRequirement().getResource().getCapabilities(
+ PackageNamespace.PACKAGE_NAMESPACE))
{
- for (Capability cap : wire.getRequirement().getResource().getCapabilities(
- null))
- {
- if (PackageNamespace.PACKAGE_NAMESPACE.equals(cap.getNamespace()))
- {
- exportNames.add((String) cap.getAttributes().get(
- PackageNamespace.PACKAGE_NAMESPACE));
- }
- }
+ exportNames.add((String) cap.getAttributes().get(
+ PackageNamespace.PACKAGE_NAMESPACE));
}
}
List<Wire> substitutionWires = new ArrayList<Wire>();
- for (Wire wire : wiring.getRequiredResourceWires(null))
+ for (Wire wire : wiring.getRequiredResourceWires(PackageNamespace.PACKAGE_NAMESPACE))
{
- if (PackageNamespace.PACKAGE_NAMESPACE.equals(
- wire.getCapability().getNamespace()))
+ if (exportNames.contains(wire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)))
{
- if (exportNames.contains(wire.getCapability().getAttributes().get(
- PackageNamespace.PACKAGE_NAMESPACE)))
- {
- substitutionWires.add(wire);
- }
+ substitutionWires.add(wire);
}
}
return substitutionWires;
Modified: felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/BundleWiringImplTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/BundleWiringImplTest.java?rev=1829110&r1=1829109&r2=1829110&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/BundleWiringImplTest.java (original)
+++ felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/BundleWiringImplTest.java Fri Apr 13 21:35:54 2018
@@ -20,6 +20,7 @@ package org.apache.felix.framework;
import org.apache.felix.framework.BundleWiringImpl.BundleClassLoader;
import org.apache.felix.framework.cache.Content;
+import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
@@ -44,6 +45,7 @@ import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -51,8 +53,16 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -655,6 +665,440 @@ public class BundleWiringImplTest
verify(requiredPkgs, never()).values();
}
+ @Test
+ public void testParallelClassload() throws Exception
+ {
+
+
+ Felix mockFramework = mock(Felix.class);
+ HookRegistry hReg = mock(HookRegistry.class);
+ Mockito.when(mockFramework.getHookRegistry()).thenReturn(hReg);
+ Content mockContent = mock(Content.class);
+ final Class testClass = TestClassSuper.class;
+ final String testClassName = testClass.getName();
+ final String testClassAsPath = testClassName.replace('.', '/') + ".class";
+ byte[] testClassBytes = createTestClassBytes(testClass, testClassAsPath);
+
+ final Class testClass2 = TestClassChild.class;
+ final String testClassName2 = testClass2.getName();
+ final String testClassAsPath2 = testClassName2.replace('.', '/') + ".class";
+ byte[] testClassBytes2 = createTestClassBytes(testClass2, testClassAsPath2);
+
+ final Class testClass3 = TestClass.class;
+ final String testClassName3 = testClass3.getName();
+ final String testClassAsPath3 = testClassName3.replace('.', '/') + ".class";
+ byte[] testClassBytes3 = createTestClassBytes(testClass3, testClassAsPath3);
+
+ List<Content> contentPath = new ArrayList<Content>();
+ contentPath.add(mockContent);
+ BundleWiringImpl bundleWiring;
+
+ StatefulResolver mockResolver;
+
+ BundleRevisionImpl mockRevisionImpl;
+
+ BundleImpl mockBundle;
+
+ mockResolver = mock(StatefulResolver.class);
+ mockRevisionImpl = mock(BundleRevisionImpl.class);
+ mockBundle = mock(BundleImpl.class);
+
+ Logger logger = new Logger();
+ Map configMap = new HashMap();
+ List<BundleRevision> fragments = new ArrayList<BundleRevision>();
+ List<BundleWire> wires = new ArrayList<BundleWire>();
+ Map<String, BundleRevision> importedPkgs = new HashMap<String, BundleRevision>();
+ Map<String, List<BundleRevision>> requiredPkgs = new HashMap<String, List<BundleRevision>>();
+
+ when(mockRevisionImpl.getBundle()).thenReturn(mockBundle);
+ when(mockBundle.getBundleId()).thenReturn(Long.valueOf(1));
+
+ bundleWiring = new BundleWiringImpl(logger, configMap, mockResolver,
+ mockRevisionImpl, fragments, wires, importedPkgs, requiredPkgs);
+
+ when(mockBundle.getFramework()).thenReturn(mockFramework);
+ when(mockFramework.getBootPackages()).thenReturn(new String[0]);
+
+ when(mockRevisionImpl.getContentPath()).thenReturn(contentPath);
+ when(mockContent.getEntryAsBytes(testClassAsPath)).thenReturn(
+ testClassBytes);
+ when(mockContent.getEntryAsBytes(testClassAsPath2)).thenReturn(
+ testClassBytes2);
+ when(mockContent.getEntryAsBytes(testClassAsPath3)).thenReturn(
+ testClassBytes3);
+
+
+ final TestBundleClassLoader bundleClassLoader = createBundleClassLoader(
+ TestBundleClassLoader.class, bundleWiring);
+ assertNotNull(bundleClassLoader);
+
+ Field m_classLoader = bundleWiring.getClass().getDeclaredField("m_classLoader");
+ m_classLoader.setAccessible(true);
+ m_classLoader.set(bundleWiring, bundleClassLoader);
+
+ assertTrue(bundleClassLoader.isParallel());
+
+ final AtomicInteger loaded = new AtomicInteger();
+ new Thread() {
+ public void run() {
+ try
+ {
+ loaded.set(bundleClassLoader.findClass(testClassName2) != null ? 1 : 2);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ loaded.set(3);
+ }
+ }
+ }.start();
+
+ while (bundleClassLoader.m_gate.getQueueLength() == 0)
+ {
+ Thread.sleep(1);
+ }
+
+ final AtomicInteger loaded2 = new AtomicInteger();
+ new Thread() {
+ public void run() {
+ try
+ {
+ loaded2.set(bundleClassLoader.findClass(testClassName3) != null ? 1 : 2);
+ }
+ catch (ClassNotFoundException e)
+ {
+ e.printStackTrace();
+ loaded2.set(3);
+ }
+ }
+ }.start();
+
+ while (loaded2.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+
+ assertEquals(0, loaded.get());
+ assertEquals(1, bundleClassLoader.m_gate.getQueueLength());
+
+ loaded2.set(0);
+ Thread tester = new Thread() {
+ public void run() {
+ try
+ {
+ loaded2.set(bundleClassLoader.findClass(testClassName2) != null ? 1 : 2);
+ }
+ catch (ClassNotFoundException e)
+ {
+ e.printStackTrace();
+ loaded2.set(3);
+ }
+ }
+ };
+ tester.start();
+
+ Thread.sleep(100);
+
+ assertEquals(0, loaded2.get());
+ assertEquals(1, bundleClassLoader.m_gate.getQueueLength());
+
+ bundleClassLoader.m_gate.release();
+
+
+ while (loaded.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+
+ assertEquals(1, loaded.get());
+
+ while (loaded2.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+ assertEquals(1, loaded2.get());
+ }
+
+ @Test
+ public void testClassloadStress() throws Exception
+ {
+ ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4);
+ final List<Throwable> exceptionsNP = Collections.synchronizedList(new ArrayList<Throwable>());
+ final List<Throwable> exceptionsP = Collections.synchronizedList(new ArrayList<Throwable>());
+
+ for (int i = 0; i < 100; i++) {
+ executors.submit(i % 2 == 0 ? new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ testNotParallelClassload();
+ }
+ catch (Throwable e)
+ {
+ exceptionsNP.add(e);
+ }
+ }
+ } : new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ testParallelClassload();
+ }
+ catch (Throwable e)
+ {
+ exceptionsP.add(e);
+ }
+ }
+ });
+ }
+ executors.shutdown();
+ executors.awaitTermination(10, TimeUnit.MINUTES);
+ assertTrue(exceptionsNP.toString(), exceptionsNP.isEmpty());
+ assertTrue(exceptionsP.toString(), exceptionsP.isEmpty());
+ }
+
+ @Test
+ public void testNotParallelClassload() throws Exception
+ {
+
+ Felix mockFramework = mock(Felix.class);
+ HookRegistry hReg = mock(HookRegistry.class);
+ Mockito.when(mockFramework.getHookRegistry()).thenReturn(hReg);
+ Content mockContent = mock(Content.class);
+ final Class testClass = TestClassSuper.class;
+ final String testClassName = testClass.getName();
+ final String testClassAsPath = testClassName.replace('.', '/') + ".class";
+ byte[] testClassBytes = createTestClassBytes(testClass, testClassAsPath);
+
+ final Class testClass2 = TestClassChild.class;
+ final String testClassName2 = testClass2.getName();
+ final String testClassAsPath2 = testClassName2.replace('.', '/') + ".class";
+ byte[] testClassBytes2 = createTestClassBytes(testClass2, testClassAsPath2);
+
+ final Class testClass3 = TestClass.class;
+ final String testClassName3 = testClass3.getName();
+ final String testClassAsPath3 = testClassName3.replace('.', '/') + ".class";
+ byte[] testClassBytes3 = createTestClassBytes(testClass3, testClassAsPath3);
+
+ List<Content> contentPath = new ArrayList<Content>();
+ contentPath.add(mockContent);
+ BundleWiringImpl bundleWiring;
+
+ StatefulResolver mockResolver;
+
+ BundleRevisionImpl mockRevisionImpl;
+
+ BundleImpl mockBundle;
+
+ mockResolver = mock(StatefulResolver.class);
+ mockRevisionImpl = mock(BundleRevisionImpl.class);
+ mockBundle = mock(BundleImpl.class);
+
+ Logger logger = new Logger();
+ Map configMap = new HashMap();
+ List<BundleRevision> fragments = new ArrayList<BundleRevision>();
+ List<BundleWire> wires = new ArrayList<BundleWire>();
+ Map<String, BundleRevision> importedPkgs = new HashMap<String, BundleRevision>();
+ Map<String, List<BundleRevision>> requiredPkgs = new HashMap<String, List<BundleRevision>>();
+
+ when(mockRevisionImpl.getBundle()).thenReturn(mockBundle);
+ when(mockBundle.getBundleId()).thenReturn(Long.valueOf(1));
+
+ bundleWiring = new BundleWiringImpl(logger, configMap, mockResolver,
+ mockRevisionImpl, fragments, wires, importedPkgs, requiredPkgs);
+
+ when(mockBundle.getFramework()).thenReturn(mockFramework);
+ when(mockFramework.getBootPackages()).thenReturn(new String[0]);
+
+ when(mockRevisionImpl.getContentPath()).thenReturn(contentPath);
+ when(mockContent.getEntryAsBytes(testClassAsPath)).thenReturn(
+ testClassBytes);
+ when(mockContent.getEntryAsBytes(testClassAsPath2)).thenReturn(
+ testClassBytes2);
+ when(mockContent.getEntryAsBytes(testClassAsPath3)).thenReturn(
+ testClassBytes3);
+
+
+ final TestBundleClassLoader2 bundleClassLoader = createBundleClassLoader(
+ TestBundleClassLoader2.class, bundleWiring);
+ assertNotNull(bundleClassLoader);
+
+ Field m_classLoader = bundleWiring.getClass().getDeclaredField("m_classLoader");
+ m_classLoader.setAccessible(true);
+ m_classLoader.set(bundleWiring, bundleClassLoader);
+
+ assertFalse(bundleClassLoader.isParallel());
+
+ final AtomicInteger loaded = new AtomicInteger();
+ new Thread() {
+ public void run() {
+ try
+ {
+ loaded.set(bundleClassLoader.findClass(testClassName2) != null ? 1 : 2);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ loaded.set(3);
+ }
+ }
+ }.start();
+
+ while (bundleClassLoader.m_gate.getQueueLength() == 0)
+ {
+ Thread.sleep(1);
+ }
+
+ final AtomicInteger loaded2 = new AtomicInteger();
+ new Thread() {
+ public void run() {
+ try
+ {
+ loaded2.set(bundleClassLoader.findClass(testClassName3) != null ? 1 : 2);
+ }
+ catch (ClassNotFoundException e)
+ {
+ e.printStackTrace();
+ loaded2.set(3);
+ }
+ }
+ }.start();
+
+ Thread.sleep(100);
+
+ assertEquals(0, loaded.get());
+ assertEquals(0, loaded2.get());
+ assertEquals(1, bundleClassLoader.m_gate.getQueueLength());
+
+ final AtomicInteger loaded3 = new AtomicInteger();
+ Thread tester = new Thread() {
+ public void run() {
+ try
+ {
+ loaded3.set(bundleClassLoader.findClass(testClassName2) != null ? 1 : 2);
+ }
+ catch (ClassNotFoundException e)
+ {
+ e.printStackTrace();
+ loaded3.set(3);
+ }
+ }
+ };
+ tester.start();
+
+ Thread.sleep(100);
+
+ assertEquals(0, loaded3.get());
+ assertEquals(0, loaded2.get());
+
+ assertEquals(0, loaded.get());
+ assertEquals(1, bundleClassLoader.m_gate.getQueueLength());
+
+ bundleClassLoader.m_gate.release();
+
+
+ while (loaded.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+
+ assertEquals(1, loaded.get());
+
+ while (loaded2.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+ assertEquals(1, loaded2.get());
+
+ while (loaded3.get() == 0)
+ {
+ Thread.sleep(1);
+ }
+ assertEquals(1, loaded3.get());
+ }
+
+ private static class TestBundleClassLoader extends BundleClassLoader
+ {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ Semaphore m_gate = new Semaphore(0);
+ public TestBundleClassLoader(BundleWiringImpl wiring, ClassLoader parent, Logger logger)
+ {
+ super(wiring, parent, logger);
+ }
+
+ @Override
+ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ if (name.startsWith("java"))
+ {
+ return getClass().getClassLoader().loadClass(name);
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class findClass(String name) throws ClassNotFoundException
+ {
+ if (name.startsWith("java"))
+ {
+ return getClass().getClassLoader().loadClass(name);
+ }
+ if (name.equals(TestClassSuper.class.getName()))
+ {
+ m_gate.acquireUninterruptibly();
+ }
+ return super.findClass(name);
+ }
+ }
+
+ private static class TestBundleClassLoader2 extends BundleClassLoader
+ {
+ Semaphore m_gate = new Semaphore(0);
+ public TestBundleClassLoader2(BundleWiringImpl wiring, ClassLoader parent, Logger logger)
+ {
+ super(wiring, parent, logger);
+ }
+
+ @Override
+ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ if (name.startsWith("java"))
+ {
+ return getClass().getClassLoader().loadClass(name);
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ protected Class findClass(String name) throws ClassNotFoundException
+ {
+ if (name.startsWith("java"))
+ {
+ return getClass().getClassLoader().loadClass(name);
+ }
+ if (name.equals(TestClassSuper.class.getName()))
+ {
+ m_gate.acquireUninterruptibly();
+ }
+ return super.findClass(name);
+ }
+
+ @Override
+ protected boolean isParallel()
+ {
+ return false;
+ }
+ }
+
@SuppressWarnings("rawtypes")
private byte[] createTestClassBytes(Class testClass, String testClassAsPath)
throws IOException
@@ -673,8 +1117,8 @@ public class BundleWiringImplTest
}
@SuppressWarnings("rawtypes")
- private BundleClassLoader createBundleClassLoader(
- Class bundleClassLoaderClass, BundleWiringImpl bundleWiring)
+ private <T> T createBundleClassLoader(
+ Class<T> bundleClassLoaderClass, BundleWiringImpl bundleWiring)
throws Exception
{
Logger logger = new Logger();
@@ -682,7 +1126,8 @@ public class BundleWiringImplTest
bundleClassLoaderClass,
new Class[] { BundleWiringImpl.class, ClassLoader.class,
Logger.class });
- BundleClassLoader bundleClassLoader = (BundleClassLoader) BundleRevisionImpl
+ BundleRevisionImpl.getSecureAction().setAccesssible(ctor);
+ T bundleClassLoader = (T) BundleRevisionImpl
.getSecureAction().invoke(
ctor,
new Object[] { bundleWiring,
@@ -695,6 +1140,16 @@ public class BundleWiringImplTest
// An empty test class to weave.
}
+ class TestClassSuper
+ {
+ // An empty test class to weave.
+ }
+
+ class TestClassChild extends TestClassSuper
+ {
+
+ }
+
class GoodDummyWovenHook implements WeavingHook
{
// Adds the awesomePublicField to a class