Apply patch to utilize parallel class loaders on Java 7. (FELIX-3553)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1441094 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index 79de81c..dde7632 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -1804,18 +1804,33 @@
         }
     }
 
-    public class BundleClassLoaderJava5 extends BundleClassLoader
+    public static class BundleClassLoaderJava5 extends BundleClassLoader
     {
-        public BundleClassLoaderJava5(ClassLoader parent)
+        static
         {
-            super(parent);
+            try
+            {
+                ClassLoader.registerAsParallelCapable();
+            }
+            catch (Throwable th)
+            {
+                // This is OK on older java versions
+            }
+        }
+
+        private BundleWiringImpl m_wiring;
+
+        public BundleClassLoaderJava5(BundleWiringImpl wiring, ClassLoader parent)
+        {
+            super(wiring, parent);
+            m_wiring = wiring;
         }
 
         @Override
         public Enumeration getResources(String name)
         {
-            Enumeration urls = BundleWiringImpl.this.getResourcesByDelegation(name);
-            if (m_useLocalURLs)
+            Enumeration urls = m_wiring.getResourcesByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
             {
                 urls = new ToLocalUrlEnumeration(urls);
             }
@@ -1825,12 +1840,24 @@
         @Override
         protected Enumeration findResources(String name)
         {
-            return m_revision.getResourcesLocal(name);
+            return m_wiring.m_revision.getResourcesLocal(name);
         }
     }
 
-    public class BundleClassLoader extends SecureClassLoader implements BundleReference
+    public static class BundleClassLoader extends SecureClassLoader implements BundleReference
     {
+         static
+         {
+             try
+             {
+                 ClassLoader.registerAsParallelCapable();
+             }
+             catch (Throwable th)
+             {
+                 // This is OK on older java versions
+             }
+         }
+
         // Flag used to determine if a class has been loaded from this class
         // loader or not.
         private volatile boolean m_isActivationTriggered = false;
@@ -1839,8 +1866,10 @@
         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 BundleWiringImpl m_wiring;
 
-        public BundleClassLoader(ClassLoader parent)
+        public BundleClassLoader(BundleWiringImpl wiring, ClassLoader parent)
         {
             super(parent);
             if (m_dexFileClassLoadClass != null)
@@ -1851,6 +1880,7 @@
             {
                 m_jarContentToDexFile = null;
             }
+            m_wiring = wiring;
         }
 
         public boolean isActivationTriggered()
@@ -1860,17 +1890,17 @@
 
         public Bundle getBundle()
         {
-            return BundleWiringImpl.this.getBundle();
+            return m_wiring.getBundle();
         }
 
         @Override
         protected Class loadClass(String name, boolean resolve)
             throws ClassNotFoundException
         {
-            Class clazz = null;
+            Class clazz;
 
             // Make sure the class was not already loaded.
-            synchronized (this)
+            synchronized (m_classLocks)
             {
                 clazz = findLoadedClass(name);
             }
@@ -1879,7 +1909,7 @@
             {
                 try
                 {
-                    clazz = (Class) findClassOrResourceByDelegation(name, true);
+                    clazz = (Class) m_wiring.findClassOrResourceByDelegation(name, true);
                 }
                 catch (ResourceNotFoundException ex)
                 {
@@ -1889,10 +1919,9 @@
                 catch (ClassNotFoundException cnfe)
                 {
                     ClassNotFoundException ex = cnfe;
-                    String msg = name;
-                    if (m_logger.getLogLevel() >= Logger.LOG_DEBUG)
+                    if (m_wiring.m_logger.getLogLevel() >= Logger.LOG_DEBUG)
                     {
-                        msg = diagnoseClassLoadError(m_resolver, m_revision, name);
+                        String msg = diagnoseClassLoadError(m_wiring.m_resolver, m_wiring.m_revision, name);
                         ex = (msg != null)
                             ? new ClassNotFoundException(msg, cnfe)
                             : ex;
@@ -1922,7 +1951,7 @@
                 byte[] bytes = null;
 
                 // Check the bundle class path.
-                List<Content> contentPath = m_revision.getContentPath();
+                List<Content> contentPath = m_wiring.m_revision.getContentPath();
                 Content content = null;
                 for (int i = 0;
                     (bytes == null) &&
@@ -1944,14 +1973,14 @@
                     // or removal, we just get a snapshot and leave any changes
                     // as a race condition, doing any necessary clean up in
                     // the error handling.
-                    Felix felix = ((BundleImpl) m_revision.getBundle()).getFramework();
+                    Felix felix = ((BundleImpl) m_wiring.m_revision.getBundle()).getFramework();
                     Set<ServiceReference<WeavingHook>> hooks =
                         felix.getHooks(WeavingHook.class);
                     WovenClassImpl wci = null;
                     if (!hooks.isEmpty())
                     {
                         // Create woven class to be used for hooks.
-                        wci = new WovenClassImpl(name, BundleWiringImpl.this, bytes);
+                        wci = new WovenClassImpl(name, m_wiring, bytes);
                         // Loop through hooks in service ranking order.
                         for (ServiceReference<WeavingHook> sr : hooks)
                         {
@@ -2000,199 +2029,225 @@
                     // 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.
-                    synchronized (this)
+                    synchronized (m_classLocks)
                     {
-                        byte[] wovenBytes = null;
-                        Class wovenClass = null;
-                        List<String> wovenImports = null;
-                        try
+                        Thread me = Thread.currentThread();
+                        while (m_classLocks.containsKey(name) && (m_classLocks.get(name) != me))
                         {
-                            clazz = findLoadedClass(name);
-                            if (clazz == null)
+                            try
                             {
-                                // 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 = wovenBytes = wci._getBytes();
-                                    wovenImports = wci.getDynamicImportsInternal();
+                                m_classLocks.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 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)
+                    byte[] wovenBytes = null;
+                    Class wovenClass = null;
+                    List<String> wovenImports = null;
+                    try
+                    {
+                        if (clazz == null)
+                        {
+                            // 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 = wovenBytes = wci._getBytes();
+                                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
                                     {
-                                        try
-                                        {
-                                            List<BundleRequirement> wovenReqs =
-                                                ManifestParser.parseDynamicImportHeader(
-                                                    m_logger, 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())
+                                        List<BundleRequirement> wovenReqs =
+                                            ManifestParser.parseDynamicImportHeader(
+                                                m_wiring.m_logger, m_wiring.m_revision, s);
+                                        allWovenReqs.addAll(wovenReqs);
+                                    }
+                                    catch (BundleException ex)
                                     {
-                                        // Check for duplicate woven imports.
-                                        // First grab existing woven imports, if any.
-                                        Set<String> filters = new HashSet<String>();
-                                        if (m_wovenReqs != null)
-                                        {
-                                            for (BundleRequirement req : 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_wovenReqs != null)
-                                            {
-                                                allWovenReqs.addAll(0, m_wovenReqs);
-                                            }
-                                            m_wovenReqs = allWovenReqs;
-                                        }
+                                        // 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)
+                                        {
+                                            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;
+                                    }
+                                }
+                            }
 
-                                int activationPolicy =
-                                    ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
+                            int activationPolicy =
+                                ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
                                     ? ((BundleRevisionImpl) getBundle()
                                         .adapt(BundleRevision.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_revision.isActivationTrigger(pkgName);
-                                if (!m_isActivationTriggered
-                                    && isTriggerClass
-                                    && (activationPolicy == BundleRevisionImpl.LAZY_ACTIVATION)
-                                    && (getBundle().getState() == Bundle.STARTING))
+                            // 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)
                                 {
-                                    List deferredList = (List) m_deferredActivation.get();
-                                    if (deferredList == null)
-                                    {
-                                        deferredList = new ArrayList();
-                                        m_deferredActivation.set(deferredList);
-                                    }
-                                    deferredList.add(new Object[] { name, getBundle() });
+                                    deferredList = new ArrayList();
+                                    m_deferredActivation.set(deferredList);
                                 }
-                                // 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)
+                                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)
                                 {
-                                    if (getPackage(pkgName) == null)
+                                    Object[] params = definePackage(pkgName);
+                                    if (params != null)
                                     {
-                                        Object[] params = definePackage(pkgName);
-                                        if (params != null)
-                                        {
-                                            definePackage(
-                                                pkgName,
-                                                (String) params[0],
-                                                (String) params[1],
-                                                (String) params[2],
-                                                (String) params[3],
-                                                (String) params[4],
-                                                (String) params[5],
-                                                null);
-                                        }
-                                        else
-                                        {
-                                            definePackage(pkgName, null, null,
-                                                null, null, null, null, null);
-                                        }
-                                    }
-                                }
-
-                                // 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 (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_revision.getProtectionDomain() != null)
-                                    {
-                                        clazz = defineClass(name, bytes, 0, bytes.length,
-                                            m_revision.getProtectionDomain());
+                                        definePackage(
+                                            pkgName,
+                                            (String) params[0],
+                                            (String) params[1],
+                                            (String) params[2],
+                                            (String) params[3],
+                                            (String) params[4],
+                                            (String) params[5],
+                                            null);
                                     }
                                     else
                                     {
-                                        clazz = defineClass(name, bytes, 0, bytes.length);
+                                        definePackage(pkgName, null, null,
+                                            null, null, null, null, null);
                                     }
-
-                                    wovenClass = clazz;
                                 }
+                            }
 
-                                // At this point if we have a trigger class, then the deferred
-                                // activation trigger has tripped.
-                                if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
+                            // If we can load the class from a dex file do so
+                            if (content instanceof JarContent)
+                            {
+                                try
                                 {
-                                    m_isActivationTriggered = true;
+                                    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);
+                                }
+
+                                wovenClass = clazz;
+                            }
+
+                            // 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;
                             }
                         }
-                        finally
+                    }
+                    finally
+                    {
+                        // If we have a woven class, mark it as complete.
+                        // Not exactly clear how we should deal with the
+                        // case where the weaving didn't happen because
+                        // someone else beat us in defining the class.
+                        if (wci != null)
                         {
-                            // If we have a woven class, mark it as complete.
-                            // Not exactly clear how we should deal with the
-                            // case where the weaving didn't happen because
-                            // someone else beat us in defining the class.
-                            if (wci != null)
-                            {
-                                wci.complete(wovenClass, wovenBytes, wovenImports);
-                            }
+                            wci.complete(wovenClass, wovenBytes, wovenImports);
+                        }
+
+                        synchronized (m_classLocks)
+                        {
+                            m_classLocks.remove(name);
+                            m_classLocks.notifyAll();
                         }
                     }
 
@@ -2217,7 +2272,7 @@
                             }
                             catch (Throwable ex)
                             {
-                                m_logger.log((BundleImpl) (lazy)[1],
+                                m_wiring.m_logger.log((BundleImpl) (lazy)[1],
                                     Logger.LOG_WARNING,
                                     "Unable to lazily start bundle.",
                                     ex);
@@ -2232,12 +2287,12 @@
 
         private Object[] definePackage(String pkgName)
         {
-            String spectitle = (String) m_revision.getHeaders().get("Specification-Title");
-            String specversion = (String) m_revision.getHeaders().get("Specification-Version");
-            String specvendor = (String) m_revision.getHeaders().get("Specification-Vendor");
-            String impltitle = (String) m_revision.getHeaders().get("Implementation-Title");
-            String implversion = (String) m_revision.getHeaders().get("Implementation-Version");
-            String implvendor = (String) m_revision.getHeaders().get("Implementation-Vendor");
+            String spectitle = (String) m_wiring.m_revision.getHeaders().get("Specification-Title");
+            String specversion = (String) m_wiring.m_revision.getHeaders().get("Specification-Version");
+            String specvendor = (String) m_wiring.m_revision.getHeaders().get("Specification-Vendor");
+            String impltitle = (String) m_wiring.m_revision.getHeaders().get("Implementation-Title");
+            String implversion = (String) m_wiring.m_revision.getHeaders().get("Implementation-Version");
+            String implvendor = (String) m_wiring.m_revision.getHeaders().get("Implementation-Vendor");
             if ((spectitle != null)
                 || (specversion != null)
                 || (specvendor != null)
@@ -2299,8 +2354,8 @@
         @Override
         public URL getResource(String name)
         {
-            URL url = BundleWiringImpl.this.getResourceByDelegation(name);
-            if (m_useLocalURLs)
+            URL url = m_wiring.getResourceByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
             {
                 url = convertToLocalUrl(url);
             }
@@ -2310,7 +2365,7 @@
         @Override
         protected URL findResource(String name)
         {
-            return m_revision.getResourceLocal(name);
+            return m_wiring.m_revision.getResourceLocal(name);
         }
 
         // The findResources() method should only look at the revision itself, but
@@ -2321,8 +2376,8 @@
         @Override
         protected Enumeration findResources(String name)
         {
-            Enumeration urls = BundleWiringImpl.this.getResourcesByDelegation(name);
-            if (m_useLocalURLs)
+            Enumeration urls = m_wiring.getResourcesByDelegation(name);
+            if (m_wiring.m_useLocalURLs)
             {
                 urls = new ToLocalUrlEnumeration(urls);
             }
@@ -2357,21 +2412,21 @@
                 // native library.
                 if (result == null)
                 {
-                    List<R4Library> libs = getNativeLibraries();
+                    List<R4Library> libs = m_wiring.getNativeLibraries();
                     for (int libIdx = 0; (libs != null) && (libIdx < libs.size()); libIdx++)
                     {
-                        if (libs.get(libIdx).match(m_configMap, name))
+                        if (libs.get(libIdx).match(m_wiring.m_configMap, name))
                         {
                             // Search bundle content first for native library.
-                            result = m_revision.getContent().getEntryAsNativeLibrary(
+                            result = m_wiring.m_revision.getContent().getEntryAsNativeLibrary(
                                 libs.get(libIdx).getEntryName());
                             // If not found, then search fragments in order.
                             for (int i = 0;
-                                (result == null) && (m_fragmentContents != null)
-                                    && (i < m_fragmentContents.size());
+                                (result == null) && (m_wiring.m_fragmentContents != null)
+                                    && (i < m_wiring.m_fragmentContents.size());
                                 i++)
                             {
-                                result = m_fragmentContents.get(i).getEntryAsNativeLibrary(
+                                result = m_wiring.m_fragmentContents.get(i).getEntryAsNativeLibrary(
                                     libs.get(libIdx).getEntryName());
                             }
                         }
@@ -2394,7 +2449,7 @@
         @Override
         public String toString()
         {
-            return BundleWiringImpl.this.toString();
+            return m_wiring.toString();
         }
     }