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


Reply via email to