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)
{