Implement byte-code weaving hooks. (FELIX-2959)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1127155 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
index 7aa4bcf..63ab79d 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
@@ -341,7 +341,8 @@
     public <S> ServiceRegistration<S> registerService(
         Class<S> clazz, S svcObj, Dictionary<String, ? > dict)
     {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return (ServiceRegistration<S>)
+            registerService(new String[] { clazz.getName() }, svcObj, dict);
     }
 
     public ServiceReference<?> getServiceReference(String clazz)
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 73722d1..41c7b0d 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -38,7 +38,10 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedMap;
+import java.util.SortedSet;
 import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.jar.Manifest;
 import org.apache.felix.framework.Felix.StatefulResolver;
 import org.apache.felix.framework.cache.JarContent;
 import org.apache.felix.framework.cache.Content;
@@ -51,6 +54,7 @@
 import org.apache.felix.framework.util.SecureAction;
 import org.apache.felix.framework.util.SecurityManagerEx;
 import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
 import org.apache.felix.framework.util.manifestparser.R4Library;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.wiring.BundleRequirementImpl;
@@ -58,6 +62,10 @@
 import org.osgi.framework.BundleException;
 import org.osgi.framework.BundleReference;
 import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.weaving.WeavingException;
+import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
@@ -82,6 +90,8 @@
     private final List<R4Library> m_resolvedNativeLibs;
     private final List<Content> m_fragmentContents;
 
+    private volatile List<BundleRequirement> m_wovenReqs = null;
+
     private BundleClassLoader m_classLoader;
     private boolean m_isActivationTriggered = false;
 
@@ -313,7 +323,7 @@
 
     public boolean isCurrent()
     {
-        throw new UnsupportedOperationException("Not supported yet.");
+        return (((BundleImpl) m_revision.getBundle()).getCurrentRevision() == m_revision);
     }
 
     public boolean isInUse()
@@ -340,12 +350,21 @@
 
     public List<BundleRequirement> getRequirements(String namespace)
     {
-
+        List<BundleRequirement> searchReqs = m_resolvedReqs;
+        List<BundleRequirement> wovenReqs = m_wovenReqs;
         List<BundleRequirement> result = m_resolvedReqs;
+
+        if (wovenReqs != null)
+        {
+            searchReqs = new ArrayList<BundleRequirement>(m_resolvedReqs);
+            searchReqs.addAll(wovenReqs);
+            result = searchReqs;
+        }
+
         if (namespace != null)
         {
             result = new ArrayList<BundleRequirement>();
-            for (BundleRequirement req : m_resolvedReqs)
+            for (BundleRequirement req : searchReqs)
             {
                 if (req.getNamespace().equals(namespace))
                 {
@@ -1336,104 +1355,261 @@
                     // Get package name.
                     String pkgName = Util.getClassPackage(name);
 
+                    // Get weaving hooks and invoke them to give them a
+                    // chance to weave the class' byte code before we
+                    // define it.
+                    // NOTE: We don't try to dynamically track hook addition
+                    // 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();
+                    SortedSet<ServiceReference<WeavingHook>> hooks =
+                        felix.getHooks(WeavingHook.class);
+                    WovenClassImpl wci = null;
+                    if ((hooks != null) && !hooks.isEmpty())
+                    {
+                        // Create woven class to be used for hooks.
+                        wci = new WovenClassImpl(name, BundleWiringImpl.this, bytes);
+                        // Loop through hooks in service ranking order.
+                        for (ServiceReference<WeavingHook> sr : hooks)
+                        {
+                            // Only use the hook if it is not black listed.
+                            if (!felix.isHookBlackListed(sr))
+                            {
+                                // Get the hook service object.
+                                // Note that we don't use the bundle context
+                                // to get the service object since that would
+                                // perform sercurity checks.
+                                WeavingHook wh = felix.getService(felix, sr);
+                                if (wh != null)
+                                {
+                                    try
+                                    {
+                                        wh.weave(wci);
+                                    }
+                                    catch (Throwable th)
+                                    {
+                                        if (!(th instanceof WeavingException))
+                                        {
+                                            felix.blackListHook(sr);
+                                        }
+                                        felix.fireFrameworkEvent(
+                                            FrameworkEvent.ERROR,
+                                            sr.getBundle(),
+                                            th);
+
+                                        // Mark the woven class as incomplete.
+                                        wci.complete(null, null, null);
+                                        // Throw class format exception per spec.
+                                        Error error = new ClassFormatError("Weaving hook failed.");
+                                        error.initCause(th);
+                                        throw error;
+                                    }
+                                    finally
+                                    {
+                                        felix.ungetService(felix, sr);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
                     // 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)
                     {
-                        clazz = findLoadedClass(name);
-
-                        if (clazz == null)
+                        byte[] wovenBytes = null;
+                        Class wovenClass = null;
+                        List<String> wovenImports = null;
+                        try
                         {
-                            int activationPolicy =
-                                ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
-                                ? ((BundleRevisionImpl) ((BundleImpl) getBundle())
-                                    .getCurrentRevision()).getDeclaredActivationPolicy()
-                                : EAGER_ACTIVATION;
+                            clazz = findLoadedClass(name);
+                            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();
 
-                            // 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))
-                            {
-                                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);
-                                    if (params != null)
+                                    // 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)
                                     {
-                                        definePackage(
-                                            pkgName,
-                                            (String) params[0],
-                                            (String) params[1],
-                                            (String) params[2],
-                                            (String) params[3],
-                                            (String) params[4],
-                                            (String) params[5],
-                                            null);
+                                        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())
+                                    {
+                                        // 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;
+                                        }
+                                    }
+                                }
+
+                                int activationPolicy =
+                                    ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
+                                    ? ((BundleRevisionImpl) ((BundleImpl) getBundle())
+                                        .getCurrentRevision()).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))
+                                {
+                                    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);
+                                        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());
                                     }
                                     else
                                     {
-                                        definePackage(pkgName, null, null,
-                                            null, null, null, null, null);
+                                        clazz = defineClass(name, bytes, 0, bytes.length);
                                     }
-                                }
-                            }
 
-                            // If we can load the class from a dex file do so
-                            if (content instanceof JarContent)
-                            {
-                                try
-                                {
-                                    clazz = getDexFileClass((JarContent) content, name, this);
+                                    wovenClass = clazz;
                                 }
-                                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)
+                                // At this point if we have a trigger class, then the deferred
+                                // activation trigger has tripped.
+                                if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
                                 {
-                                    clazz = defineClass(name, bytes, 0, bytes.length,
-                                        m_revision.getProtectionDomain());
-                                }
-                                else
-                                {
-                                    clazz = defineClass(name, bytes, 0, bytes.length);
-                                }
-                            }
-
-                            // At this point if we have a trigger class, then the deferred
-                            // activation trigger has tripped.
-                            if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
-                            {
 // TODO: OSGi R4.3 - This isn't protected by the correct lock.
-                                m_isActivationTriggered = true;
+                                    m_isActivationTriggered = true;
+                                }
+                            }
+                        }
+                        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)
+                            {
+                                wci.complete(wovenClass, wovenBytes, wovenImports);
                             }
                         }
                     }
@@ -1449,7 +1625,7 @@
                         {
                             try
                             {
-                                ((BundleImpl) ((Object[]) deferredList.get(i))[1]).getFramework().activateBundle(
+                                felix.getFramework().activateBundle(
                                     (BundleImpl) ((Object[]) deferredList.get(i))[1], true);
                             }
                             catch (BundleException ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 6a519f3..387c334 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3234,6 +3234,25 @@
     }
 
     //
+    // Hook service management methods.
+    //
+
+    boolean isHookBlackListed(ServiceReference sr)
+    {
+        return m_registry.isHookBlackListed(sr);
+    }
+
+    void blackListHook(ServiceReference sr)
+    {
+        m_registry.blackListHook(sr);
+    }
+
+    public <S> SortedSet<ServiceReference<S>> getHooks(Class<S> hookClass)
+    {
+        return m_registry.getHooks(hookClass);
+    }
+
+    //
     // PackageAdmin related methods.
     //
 
@@ -3979,7 +3998,7 @@
     /**
      * Fires bundle events.
     **/
-    private void fireFrameworkEvent(
+    void fireFrameworkEvent(
         int type, Bundle bundle, Throwable throwable)
     {
         m_dispatcher.fireFrameworkEvent(new FrameworkEvent(type, bundle, throwable));
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index 1dfe6a5..1de5e35 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -25,6 +25,7 @@
 
 import org.osgi.framework.*;
 import org.osgi.framework.hooks.service.*;
+import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.framework.launch.Framework;
 import org.osgi.framework.wiring.BundleCapability;
 
@@ -46,9 +47,21 @@
 
     private final ServiceRegistryCallbacks m_callbacks;
 
-    private final Set m_eventHooks = new TreeSet(Collections.reverseOrder());
-    private final Set m_findHooks = new TreeSet(Collections.reverseOrder());
-    private final Set m_listenerHooks = new TreeSet(Collections.reverseOrder());
+    private final WeakHashMap<ServiceReference, ServiceReference> m_blackList =
+        new WeakHashMap<ServiceReference, ServiceReference>();
+
+    private final Class<?>[] m_hookClasses = {
+        WeavingHook.class
+    };
+    private final Map<Class<?>, Set<ServiceReference<?>>> m_allHooks =
+        new HashMap<Class<?>, Set<ServiceReference<?>>>();
+
+    private final Set<ServiceReference> m_eventHooks =
+        new TreeSet<ServiceReference>(Collections.reverseOrder());
+    private final Set<ServiceReference> m_findHooks =
+        new TreeSet<ServiceReference>(Collections.reverseOrder());
+    private final Set<ServiceReference> m_listenerHooks =
+        new TreeSet<ServiceReference>(Collections.reverseOrder());
 
     public ServiceRegistry(Logger logger, ServiceRegistryCallbacks callbacks)
     {
@@ -92,7 +105,7 @@
             // Create the service registration.
             reg = new ServiceRegistrationImpl(
                 this, bundle, classNames, new Long(m_currentServiceId++), svcObj, dict);
-                        
+
             // Keep track of registered hooks.
             addHooks(classNames, svcObj, reg.getReference());
 
@@ -445,7 +458,7 @@
     }
 
     public synchronized Bundle[] getUsingBundles(ServiceReference ref)
-    {        
+    {
         Bundle[] bundles = null;
         for (Iterator iter = m_inUseMap.entrySet().iterator(); iter.hasNext(); )
         {
@@ -637,8 +650,37 @@
         }
     }
 
-    private void addHooks(String[] classNames, Object svcObj, ServiceReference ref)
+    //
+    // Hook-related methods.
+    //
+
+    boolean isHookBlackListed(ServiceReference sr)
     {
+        return m_blackList.containsKey(sr);
+    }
+
+    void blackListHook(ServiceReference sr)
+    {
+        m_blackList.put(sr, sr);
+    }
+
+    private void addHooks(String[] classNames, Object svcObj, ServiceReference<?> ref)
+    {
+        Class<?> hookClass = isHook(classNames, m_hookClasses, svcObj);
+        if (hookClass != null)
+        {
+            synchronized (m_allHooks)
+            {
+                Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+                if (hooks == null)
+                {
+                    hooks = new HashSet<ServiceReference<?>>();
+                    m_allHooks.put(hookClass, hooks);
+                }
+                hooks.add(ref);
+            }
+        }
+
         if (isHook(classNames, EventHook.class, svcObj))
         {
             synchronized (m_eventHooks)
@@ -663,14 +705,47 @@
             }
         }
     }
-    
+
+    static Class<?> isHook(String[] classNames, Class<?>[] hookClasses, Object svcObj)
+    {
+        for (Class<?> hookClass : hookClasses)
+        {
+            // For a service factory, we can only match names.
+            if (svcObj instanceof ServiceFactory)
+            {
+                for (String className : classNames)
+                {
+                    if (className.equals(hookClass.getName()))
+                    {
+                        return hookClass;
+                    }
+                }
+            }
+
+            // For a service object, check if its class matches.
+            if (hookClass.isAssignableFrom(svcObj.getClass()))
+            {
+                // But still only if it is registered under that interface.
+                String hookName = hookClass.getName();
+                for (String className : classNames)
+                {
+                    if (className.equals(hookName))
+                    {
+                        return hookClass;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     static boolean isHook(String[] classNames, Class hookClass, Object svcObj)
     {
-        if (svcObj instanceof ServiceFactory) 
+        if (svcObj instanceof ServiceFactory)
         {
             return Arrays.asList(classNames).contains(hookClass.getName());
         }
-        
+
         if (hookClass.isAssignableFrom(svcObj.getClass()))
         {
             String hookName = hookClass.getName();
@@ -690,16 +765,33 @@
         Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref)
             .getRegistration().getService();
         String [] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS);
-        
-        if (isHook(classNames, EventHook.class, svcObj)) 
+
+        Class hookClass = isHook(classNames, m_hookClasses, svcObj);
+        if (hookClass != null)
         {
-            synchronized (m_eventHooks) 
+            synchronized (m_allHooks)
+            {
+                Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+                if (hooks != null)
+                {
+                    hooks.remove(ref);
+                    if (hooks.isEmpty())
+                    {
+                        m_allHooks.remove(hookClass);
+                    }
+                }
+            }
+        }
+
+        if (isHook(classNames, EventHook.class, svcObj))
+        {
+            synchronized (m_eventHooks)
             {
                 m_eventHooks.remove(ref);
             }
         }
-        
-        if (isHook(classNames, FindHook.class, svcObj)) 
+
+        if (isHook(classNames, FindHook.class, svcObj))
         {
             synchronized (m_findHooks)
             {
@@ -716,6 +808,27 @@
         }
     }
 
+    public <S> SortedSet<ServiceReference<S>> getHooks(Class<S> hookClass)
+    {
+        synchronized (m_allHooks)
+        {
+            Set<ServiceReference<?>> hooks = m_allHooks.get(hookClass);
+            if (hooks != null)
+            {
+                SortedSet sorted = new TreeSet<ServiceReference<?>>(Collections.reverseOrder());
+                sorted.addAll(hooks);
+                return asTypedSortedSet(sorted);
+            }
+            return null;
+        }
+    }
+
+    private static <S> SortedSet<ServiceReference<S>> asTypedSortedSet(
+        SortedSet<ServiceReference<?>> ss)
+    {
+        return (SortedSet<ServiceReference<S>>) (SortedSet) ss;
+    }
+
     public List getEventHooks()
     {
         synchronized (m_eventHooks)
@@ -739,21 +852,21 @@
             return new ArrayList(m_listenerHooks);
         }
     }
-    
+
     /**
      * Invokes a Service Registry Hook
      * @param ref The ServiceReference associated with the hook to be invoked, the hook
      *        service object will be obtained through this object.
      * @param framework The framework that is invoking the hook, typically the Felix object.
-     * @param callback This is a callback object that is invoked with the actual hook object to 
+     * @param callback This is a callback object that is invoked with the actual hook object to
      *        be used, either the plain hook, or one obtained from the ServiceFactory.
      */
     public void invokeHook(
         ServiceReference ref, Framework framework, InvokeHookCallback callback)
     {
         Object hook = getService(framework, ref);
-        
-        try 
+
+        try
         {
             callback.invokeHook(hook);
         }
@@ -762,12 +875,12 @@
             m_logger.log(ref, Logger.LOG_WARNING,
                 "Problem invoking Service Registry Hook", th);
         }
-        finally 
+        finally
         {
             ungetService(framework, ref);
         }
-    }    
-    
+    }
+
     private static class UsageCount
     {
         public int m_count = 0;
diff --git a/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
new file mode 100644
index 0000000..62522a0
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.framework.util.manifestparser.ParsedHeaderClause;
+import org.osgi.framework.Constants;
+import org.osgi.framework.hooks.weaving.WovenClass;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleWiring;
+
+class WovenClassImpl implements WovenClass, List<String>
+{
+    private final String m_className;
+    private final BundleWiring m_wiring;
+    private byte[] m_bytes;
+    private List<String> m_imports = new ArrayList<String>();
+    private Class m_definedClass = null;
+    private boolean m_isComplete = false;
+
+    /* package */ WovenClassImpl(String className, BundleWiring wiring, byte[] bytes)
+    {
+        m_className = className;
+        m_wiring = wiring;
+        m_bytes = bytes;
+    }
+
+    synchronized void complete(Class definedClass, byte[] bytes, List<String> imports)
+    {
+        m_isComplete = true;
+        m_definedClass = definedClass;
+        m_bytes = (bytes == null) ? m_bytes : bytes;
+        m_imports = (imports == null)
+            ? Collections.unmodifiableList(m_imports)
+            : Collections.unmodifiableList(imports);
+    }
+
+    public synchronized byte[] getBytes()
+    {
+        byte[] bytes = m_bytes;
+        if (m_isComplete)
+        {
+            bytes = new byte[m_bytes.length];
+            System.arraycopy(m_bytes, 0, bytes, 0, m_bytes.length);
+        }
+        return bytes;
+    }
+
+    public synchronized void setBytes(byte[] bytes)
+    {
+        if (m_isComplete)
+        {
+            throw new IllegalStateException(
+                "Cannot change bytes after class weaving is completed.");
+        }
+        else
+        {
+            m_bytes = bytes;
+        }
+    }
+
+    synchronized List<String> getDynamicImportsInternal()
+    {
+        return m_imports;
+    }
+
+    public synchronized List<String> getDynamicImports()
+    {
+        return this;
+    }
+
+    public synchronized boolean isWeavingComplete()
+    {
+        return m_isComplete;
+    }
+
+    public String getClassName()
+    {
+        return m_className;
+    }
+
+    public ProtectionDomain getProtectionDomain()
+    {
+        return ((BundleImpl) m_wiring.getRevision().getBundle()).getProtectionDomain();
+    }
+
+    public synchronized Class<?> getDefinedClass()
+    {
+        return m_definedClass;
+    }
+
+    public BundleWiring getBundleWiring()
+    {
+        return m_wiring;
+    }
+
+    //
+    // List<String> implementation for dynamic imports.
+    //
+    // Design-wise this could be separated out into a separate type,
+    // but since it will only ever be used for this purpose it didn't
+    // appear to make much sense to introduce another type for it.
+
+    public synchronized int size()
+    {
+        return m_imports.size();
+    }
+
+    public synchronized boolean isEmpty()
+    {
+        return m_imports.isEmpty();
+    }
+
+    public synchronized boolean contains(Object o)
+    {
+        return m_imports.contains(o);
+    }
+
+    public synchronized Iterator<String> iterator()
+    {
+        return m_imports.iterator();
+    }
+
+    public synchronized Object[] toArray()
+    {
+        return m_imports.toArray();
+    }
+
+    public synchronized <T> T[] toArray(T[] ts)
+    {
+        return m_imports.toArray(ts);
+    }
+
+    public synchronized boolean add(String s)
+    {
+        if (s != null)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+            return m_imports.add(s);
+        }
+        return false;
+    }
+
+    public synchronized boolean remove(Object o)
+    {
+        return m_imports.remove(o);
+    }
+
+    public synchronized boolean containsAll(Collection<?> collection)
+    {
+        return m_imports.containsAll(collection);
+    }
+
+    public synchronized boolean addAll(Collection<? extends String> collection)
+    {
+        for (String s : collection)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+        }
+        return m_imports.addAll(collection);
+    }
+
+    public synchronized boolean addAll(int i, Collection<? extends String> collection)
+    {
+        for (String s : collection)
+        {
+            try
+            {
+                List<BundleRequirement> reqs =
+                    ManifestParser.parseDynamicImportHeader(null, null, s);
+            }
+            catch (Exception ex)
+            {
+                RuntimeException re =
+                    new IllegalArgumentException("Unable to parse dynamic import.");
+                re.initCause(ex);
+                throw re;
+            }
+        }
+        return m_imports.addAll(i, collection);
+    }
+
+    public synchronized boolean removeAll(Collection<?> collection)
+    {
+        return m_imports.removeAll(collection);
+    }
+
+    public synchronized boolean retainAll(Collection<?> collection)
+    {
+        return m_imports.retainAll(collection);
+    }
+
+    public synchronized void clear()
+    {
+        m_imports.clear();
+    }
+
+    public synchronized String get(int i)
+    {
+        return m_imports.get(i);
+    }
+
+    public synchronized String set(int i, String s)
+    {
+        try
+        {
+            List<BundleRequirement> reqs =
+                ManifestParser.parseDynamicImportHeader(null, null, s);
+        }
+        catch (Exception ex)
+        {
+            RuntimeException re =
+                new IllegalArgumentException("Unable to parse dynamic import.");
+            re.initCause(ex);
+            throw re;
+        }
+        return m_imports.set(i, s);
+    }
+
+    public synchronized void add(int i, String s)
+    {
+        try
+        {
+            List<BundleRequirement> reqs =
+                ManifestParser.parseDynamicImportHeader(null, null, s);
+        }
+        catch (Exception ex)
+        {
+            RuntimeException re =
+                new IllegalArgumentException("Unable to parse dynamic import.");
+            re.initCause(ex);
+            throw re;
+        }
+        m_imports.add(i, s);
+    }
+
+    public synchronized String remove(int i)
+    {
+        return m_imports.remove(i);
+    }
+
+    public synchronized int indexOf(Object o)
+    {
+        return m_imports.indexOf(o);
+    }
+
+    public synchronized int lastIndexOf(Object o)
+    {
+        return m_imports.lastIndexOf(o);
+    }
+
+    public synchronized ListIterator<String> listIterator()
+    {
+        return m_imports.listIterator();
+    }
+
+    public synchronized ListIterator<String> listIterator(int i)
+    {
+        return m_imports.listIterator(i);
+    }
+
+    public synchronized List<String> subList(int i, int i1)
+    {
+        return m_imports.subList(i, i1);
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index ce075ad..a6cd125 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -367,6 +367,17 @@
         return clauses;
     }
 
+    public static List<BundleRequirement> parseDynamicImportHeader(
+        Logger logger, BundleRevision owner, String header)
+        throws BundleException
+    {
+
+        List<ParsedHeaderClause> importClauses = parseStandardHeader(header);
+        importClauses = normalizeDynamicImportClauses(logger, importClauses, "2");
+        List<BundleRequirement> reqs = convertImports(importClauses, owner);
+        return reqs;
+    }
+
     private static List<BundleRequirement> convertImports(
         List<ParsedHeaderClause> clauses, BundleRevision owner)
     {