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