Author: pauls Date: Fri Mar 16 13:44:36 2018 New Revision: 1826988 URL: http://svn.apache.org/viewvc?rev=1826988&view=rev Log: FELIX-5797: Allow extension bundles to have imports and handle them like fragments would be handled (as required by the R7 spec). Furthermore, fix some bugs in the extension bundle support while we go along.
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleImpl.java 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/ExtensionManager.java felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/Felix.java felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleImpl.java?rev=1826988&r1=1826987&r2=1826988&view=diff ============================================================================== --- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleImpl.java (original) +++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/BundleImpl.java Fri Mar 16 13:44:36 2018 @@ -1228,14 +1228,11 @@ class BundleImpl implements Bundle, Bund synchronized boolean rollbackRevise() throws Exception { - boolean isExtension = isExtension(); BundleRevision br = m_revisions.remove(0); - if (!isExtension) - { - // Since revising a bundle adds a revision to the global - // state, we must remove it from the global state on rollback. - getFramework().getResolver().removeRevision(br); - } + // Since revising a bundle adds a revision to the global + // state, we must remove it from the global state on rollback. + getFramework().getResolver().removeRevision(br); + return m_archive.rollbackRevise(); } @@ -1257,14 +1254,9 @@ class BundleImpl implements Bundle, Bund throw ex; } - // TODO: REFACTOR - consider nulling capabilities for extension bundles - // so we don't need this check anymore. - if (!isExtension()) - { - // Now that the revision is added to the bundle, we can update - // the resolver's state to be aware of any new capabilities. - getFramework().getResolver().addRevision(revision); - } + /// Now that the revision is added to the bundle, we can update + // the resolver's state to be aware of any new capabilities. + getFramework().getResolver().addRevision(revision); } private BundleRevisionImpl createRevision(boolean isUpdate) throws Exception 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=1826988&r1=1826987&r2=1826988&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 Mar 16 13:44:36 2018 @@ -62,6 +62,7 @@ public class BundleRevisionImpl implemen private final Version m_version; private final List<BundleCapability> m_declaredCaps; + private final List<BundleCapability> m_declaredExtensionCaps; private final List<BundleRequirement> m_declaredReqs; private final List<NativeLibrary> m_declaredNativeLibs; private final int m_declaredActivationPolicy; @@ -97,6 +98,7 @@ public class BundleRevisionImpl implemen m_isFragment = false; m_version = null; m_declaredCaps = Collections.emptyList(); + m_declaredExtensionCaps = Collections.emptyList(); m_declaredReqs = Collections.emptyList(); m_declaredNativeLibs = null; m_declaredActivationPolicy = EAGER_ACTIVATION; @@ -122,9 +124,13 @@ public class BundleRevisionImpl implemen // Record some of the parsed metadata. Note, if this is an extension // bundle it's exports are removed, since they will be added to the // system bundle directly later on. + + m_isExtension = mp.isExtension(); m_manifestVersion = mp.getManifestVersion(); m_version = mp.getBundleVersion(); m_declaredCaps = mp.getCapabilities(); + m_declaredExtensionCaps = m_isExtension ? ManifestParser.aliasSymbolicName(mp.getExtensionCapabilites(), + bundle.getFramework().adapt(BundleRevisionImpl.class)) : Collections.EMPTY_LIST; m_declaredReqs = mp.getRequirements(); m_declaredNativeLibs = mp.getLibraries(); m_declaredActivationPolicy = mp.getActivationPolicy(); @@ -135,7 +141,6 @@ public class BundleRevisionImpl implemen ? null : ManifestParser.parseDelimitedString(mp.getActivationIncludeDirective(), ","); m_symbolicName = mp.getSymbolicName(); - m_isExtension = mp.isExtension(); m_isFragment = m_headerMap.containsKey(Constants.FRAGMENT_HOST); } @@ -212,6 +217,23 @@ public class BundleRevisionImpl implemen { if (cap.getNamespace().equals(namespace)) { + result.add(cap); + } + } + } + return result; + } + + public List<BundleCapability> getDeclaredExtensionCapabilities(String namespace) + { + List<BundleCapability> result = m_declaredExtensionCaps; + if (namespace != null) + { + result = new ArrayList<BundleCapability>(); + for (BundleCapability cap : m_declaredExtensionCaps) + { + if (cap.getNamespace().equals(namespace)) + { result.add(cap); } } 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=1826988&r1=1826987&r2=1826988&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 Mar 16 13:44:36 2018 @@ -174,13 +174,12 @@ public class BundleWiringImpl implements private volatile ConcurrentHashMap<String, ClassLoader> m_accessorLookupCache; BundleWiringImpl( - Logger logger, Map configMap, StatefulResolver resolver, - BundleRevisionImpl revision, List<BundleRevision> fragments, - List<BundleWire> wires, - Map<String, BundleRevision> importedPkgs, - Map<String, List<BundleRevision>> requiredPkgs) - throws Exception - { + Logger logger, Map configMap, StatefulResolver resolver, + BundleRevisionImpl revision, List<BundleRevision> fragments, + List<BundleWire> wires, + Map<String, BundleRevision> importedPkgs, + Map<String, List<BundleRevision>> requiredPkgs) throws Exception + { m_logger = logger; m_configMap = configMap; m_resolver = resolver; @@ -423,52 +422,50 @@ public class BundleWiringImpl implements List<NativeLibrary> libList = (m_revision.getDeclaredNativeLibraries() == null) ? new ArrayList<NativeLibrary>() : new ArrayList<NativeLibrary>(m_revision.getDeclaredNativeLibraries()); - for (int fragIdx = 0; - (m_fragments != null) && (fragIdx < m_fragments.size()); - fragIdx++) - { - List<NativeLibrary> libs = - ((BundleRevisionImpl) m_fragments.get(fragIdx)) - .getDeclaredNativeLibraries(); - for (int reqIdx = 0; - (libs != null) && (reqIdx < libs.size()); - reqIdx++) - { - libList.add(libs.get(reqIdx)); - } - } - // We need to return null here if we don't have any libraries, since a - // zero-length array is used to indicate that matching native libraries - // could not be found when resolving the bundle. - m_resolvedNativeLibs = (libList.isEmpty()) - ? null - : ImmutableList.newInstance(libList); + for (int fragIdx = 0; + (m_fragments != null) && (fragIdx < m_fragments.size()); + fragIdx++) + { + List<NativeLibrary> libs = + ((BundleRevisionImpl) m_fragments.get(fragIdx)) + .getDeclaredNativeLibraries(); + for (int reqIdx = 0; + (libs != null) && (reqIdx < libs.size()); + reqIdx++) + { + libList.add(libs.get(reqIdx)); + } + } + // We need to return null here if we don't have any libraries, since a + // zero-length array is used to indicate that matching native libraries + // could not be found when resolving the bundle. + m_resolvedNativeLibs = (libList.isEmpty()) ? null : ImmutableList.newInstance(libList); - ClassLoader bootLoader = m_defBootClassLoader; - if (revision.getBundle().getBundleId() != 0) + ClassLoader bootLoader = m_defBootClassLoader; + if (revision.getBundle().getBundleId() != 0) + { + Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP); + if (map instanceof Map) + { + Object l = ((Map) map).get(m_revision.getBundle()); + if (l instanceof ClassLoader) { - Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP); - if (map instanceof Map) - { - Object l = ((Map) map).get(m_revision.getBundle()); - if (l instanceof ClassLoader) - { - bootLoader = (ClassLoader) l; - } - } + bootLoader = (ClassLoader) l; } - m_bootClassLoader = bootLoader; + } + } + m_bootClassLoader = bootLoader; - m_implicitBootDelegation = - (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null) - || Boolean.valueOf( - (String) m_configMap.get( - FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue(); - - m_useLocalURLs = - (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null) - ? false : true; - } + m_implicitBootDelegation = + (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null) + || Boolean.valueOf( + (String) m_configMap.get( + FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue(); + + m_useLocalURLs = + (m_configMap.get(FelixConstants.USE_LOCALURLS_PROP) == null) + ? false : true; + } private static List<List<String>> parsePkgFilters(BundleCapability cap, String filtername) { @@ -672,18 +669,22 @@ public class BundleWiringImpl implements // Make new wires list. List<BundleWire> wires = new ArrayList<BundleWire>(m_wires); wires.add(wire); - // Make new imported package map. - Map<String, BundleRevision> importedPkgs = - new HashMap<String, BundleRevision>(m_importedPkgs); - importedPkgs.put( - (String) wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE), - wire.getProviderWiring().getRevision()); + if (wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE) != null) + { + // Make new imported package map. + Map<String, BundleRevision> importedPkgs = + new HashMap<String, BundleRevision>(m_importedPkgs); + importedPkgs.put( + (String) wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE), + wire.getProviderWiring().getRevision()); + + m_importedPkgs = importedPkgs; + } // Update associated member values. // Technically, there is a window here where readers won't see // both values updates at the same time, but it seems unlikely // to cause any issues. m_wires = ImmutableList.newInstance(wires); - m_importedPkgs = importedPkgs; } @Override Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java?rev=1826988&r1=1826987&r2=1826988&view=diff ============================================================================== --- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java (original) +++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java Fri Mar 16 13:44:36 2018 @@ -30,6 +30,7 @@ import org.apache.felix.framework.util.m import org.apache.felix.framework.util.manifestparser.NativeLibrary; import org.apache.felix.framework.util.manifestparser.NativeLibraryClause; import org.apache.felix.framework.wiring.BundleCapabilityImpl; +import org.apache.felix.framework.wiring.BundleRequirementImpl; import org.apache.felix.framework.wiring.BundleWireImpl; import org.osgi.framework.AdminPermission; import org.osgi.framework.Bundle; @@ -39,6 +40,7 @@ import org.osgi.framework.BundleExceptio import org.osgi.framework.Constants; import org.osgi.framework.FrameworkEvent; import org.osgi.framework.Version; +import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; import org.osgi.framework.namespace.NativeNamespace; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRequirement; @@ -59,6 +61,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -66,6 +69,7 @@ import java.util.NoSuchElementException; import java.util.Properties; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; /** * The ExtensionManager class is used in several ways. @@ -162,12 +166,16 @@ class ExtensionManager implements Conten private final Logger m_logger; private final Map m_configMap; private final Map m_headerMap = new StringMap(); + private final Map m_originalHeaderMap = new StringMap(); private final BundleRevisionImpl m_systemBundleRevision; private volatile List<BundleCapability> m_capabilities = Collections.EMPTY_LIST; private volatile Set<String> m_exportNames = Collections.EMPTY_SET; private volatile Object m_securityContext = null; private final List<ExtensionTuple> m_extensionTuples = Collections.synchronizedList(new ArrayList<ExtensionTuple>()); + private final List<BundleRevisionImpl> m_resolvedExtensions = new CopyOnWriteArrayList<BundleRevisionImpl>(); + private final List<BundleRevisionImpl> m_unresolvedExtensions = new CopyOnWriteArrayList<BundleRevisionImpl>(); + private static class ExtensionTuple { private final BundleActivator m_activator; @@ -256,11 +264,12 @@ class ExtensionManager implements Conten syscaps = ((capextra == null) || (capextra.trim().length() == 0)) ? syscaps : syscaps + (capextra.trim().startsWith(",") ? capextra : "," + capextra); m_headerMap.put(FelixConstants.PROVIDE_CAPABILITY, syscaps); + m_originalHeaderMap.putAll(m_headerMap); try { ManifestParser mp = new ManifestParser( m_logger, m_configMap, m_systemBundleRevision, m_headerMap); - List<BundleCapability> caps = aliasSymbolicName(mp.getCapabilities()); + List<BundleCapability> caps = ManifestParser.aliasSymbolicName(mp.getCapabilities(), m_systemBundleRevision); caps.add(buildNativeCapabilites()); appendCapabilities(caps); } @@ -307,83 +316,11 @@ class ExtensionManager implements Conten return new BundleCapabilityImpl(getRevision(), NativeNamespace.NATIVE_NAMESPACE, Collections.<String, String> emptyMap(), attributes); } - private static List<BundleCapability> aliasSymbolicName(List<BundleCapability> caps) - { - if (caps == null) - { - return new ArrayList<BundleCapability>(0); - } - - List<BundleCapability> aliasCaps = new ArrayList<BundleCapability>(caps); - - String[] aliases = { - FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME, - Constants.SYSTEM_BUNDLE_SYMBOLICNAME }; - - for (int capIdx = 0; capIdx < aliasCaps.size(); capIdx++) - { - BundleCapability cap = aliasCaps.get(capIdx); - - // Need to alias bundle and host capabilities. - if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) - || cap.getNamespace().equals(BundleRevision.HOST_NAMESPACE)) - { - // Make a copy of the attribute array. - Map<String, Object> aliasAttrs = - new HashMap<String, Object>(cap.getAttributes()); - // Add the aliased value. - aliasAttrs.put(cap.getNamespace(), aliases); - // Create the aliased capability to replace the old capability. - cap = new BundleCapabilityImpl( - cap.getRevision(), - cap.getNamespace(), - cap.getDirectives(), - aliasAttrs); - aliasCaps.set(capIdx, cap); - } - - // Further, search attributes for bundle symbolic name and alias it too. - for (Entry<String, Object> entry : cap.getAttributes().entrySet()) - { - // If there is a bundle symbolic name attribute, add the - // standard alias as a value. - if (entry.getKey().equalsIgnoreCase(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) - { - // Make a copy of the attribute array. - Map<String, Object> aliasAttrs = - new HashMap<String, Object>(cap.getAttributes()); - // Add the aliased value. - aliasAttrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, aliases); - // Create the aliased capability to replace the old capability. - aliasCaps.set(capIdx, new BundleCapabilityImpl( - cap.getRevision(), - cap.getNamespace(), - cap.getDirectives(), - aliasAttrs)); - // Continue with the next capability. - break; - } - } - } - - return aliasCaps; - } - public BundleRevisionImpl getRevision() { return m_systemBundleRevision; } - public Object getSecurityContext() - { - return m_securityContext; - } - - public synchronized void setSecurityContext(Object securityContext) - { - m_securityContext = securityContext; - } - /** * Add an extension bundle. The bundle will be added to the parent classloader * and it's exported packages will be added to the module definition @@ -398,8 +335,7 @@ class ExtensionManager implements Conten * AdminPermission.EXTENSIONLIFECYCLE and security is enabled. * @throws Exception in case something goes wrong. */ - synchronized void addExtensionBundle(Felix felix, BundleImpl bundle) - throws SecurityException, BundleException, Exception + synchronized void addExtensionBundle(Felix felix, BundleImpl bundle) throws Exception { Object sm = System.getSecurityManager(); if (sm != null) @@ -419,29 +355,20 @@ class ExtensionManager implements Conten final ClassPathExtenderFactory.ClassPathExtender extender; - if (m_extenderBoot != null && Constants.EXTENSION_BOOTCLASSPATH.equals(directive)) - { - extender = m_extenderBoot; - } - // We only support classpath extensions (not bootclasspath). - else if (!Constants.EXTENSION_FRAMEWORK.equals(directive)) + if (!Constants.EXTENSION_FRAMEWORK.equals(directive)) { throw new BundleException("Unsupported Extension Bundle type: " + - directive, new UnsupportedOperationException( - "Unsupported Extension Bundle type!")); + directive, new UnsupportedOperationException( + "Unsupported Extension Bundle type!")); } else if (m_extenderFramework == null) { // We don't support extensions m_logger.log(bundle, Logger.LOG_WARNING, - "Unable to add extension bundle - Maybe ClassLoader is not supported (on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?"); + "Unable to add extension bundle - Maybe ClassLoader is not supported (on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?"); throw new UnsupportedOperationException( - "Unable to add extension bundle."); - } - else - { - extender = m_extenderFramework; + "Unable to add extension bundle."); } Content content = bundle.adapt(BundleRevisionImpl.class).getContent(); @@ -467,73 +394,153 @@ class ExtensionManager implements Conten throw new UnsupportedOperationException( "Unable to add extension bundle."); } + + BundleRevisionImpl bri = bundle.adapt(BundleRevisionImpl.class); + + bri.resolve(null); + + m_unresolvedExtensions.add(bri); + } + + public void removeExtensionBundles(Felix felix) + { + m_resolvedExtensions.clear(); + m_unresolvedExtensions.clear(); + m_headerMap.clear(); + m_headerMap.putAll(m_originalHeaderMap); try { - // Merge the exported packages with the exported packages of the systembundle. - List<BundleCapability> exports = null; - if (Constants.EXTENSION_FRAMEWORK.equals(directive)) - { - try + ManifestParser mp = new ManifestParser( + m_logger, m_configMap, m_systemBundleRevision, m_headerMap); + List<BundleCapability> caps = ManifestParser.aliasSymbolicName(mp.getCapabilities(), m_systemBundleRevision); + caps.add(buildNativeCapabilites()); + appendCapabilities(caps); + } + catch (Exception ex) + { + m_capabilities = Collections.EMPTY_LIST; + m_logger.log( + Logger.LOG_ERROR, + "Error parsing system bundle export statement", ex); + } + } + + public List<Bundle> resolveExtensionBundles(Felix felix) + { + if (m_unresolvedExtensions.isEmpty()) + { + return Collections.emptyList(); + } + + // Collect all resolved extensions and the highest version of unresolved that are not already resolved bsn + List<BundleRevisionImpl> extensions = new ArrayList<BundleRevisionImpl>(m_resolvedExtensions); + + outer : for (BundleRevisionImpl revision : m_unresolvedExtensions) + { + if (revision.getWiring() == null) { + for (BundleRevisionImpl existing : extensions) { - exports = ManifestParser.parseExportHeader( - m_logger, m_systemBundleRevision, - (String) bundle.adapt(BundleRevisionImpl.class).getHeaders().get(Constants.EXPORT_PACKAGE), - m_systemBundleRevision.getSymbolicName(), m_systemBundleRevision.getVersion()); - exports = aliasSymbolicName(exports); + if (existing.getSymbolicName().equals(revision.getSymbolicName())) + { + continue outer; + } } - catch (Exception ex) + for (BundleRevisionImpl other : m_unresolvedExtensions) { - m_logger.log( - bundle, - Logger.LOG_ERROR, - "Error parsing extension bundle export statement: " - + bundle.adapt(BundleRevisionImpl.class).getHeaders().get(Constants.EXPORT_PACKAGE), ex); - return; + if ((revision != other) && (revision.getSymbolicName().equals(other.getSymbolicName())) && + revision.getVersion().compareTo(other.getVersion()) < 0) + { + continue outer; + } } + + extensions.add(revision); + } + } + + Map<BundleRevisionImpl, List<BundleWire>> wirings = findResolvableExtensions(extensions); + + List<Bundle> result = new ArrayList<Bundle>(); + + for (Map.Entry<BundleRevisionImpl, List<BundleWire>> entry : wirings.entrySet()) + { + BundleRevisionImpl revision = entry.getKey(); + + m_unresolvedExtensions.remove(revision); + m_resolvedExtensions.add(revision); + + BundleWire wire = new BundleWireImpl(revision, + revision.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE).get(0), + m_systemBundleRevision, getCapabilities(BundleRevision.HOST_NAMESPACE).get(0)); + + try + { + revision.resolve(new BundleWiringImpl(m_logger, m_configMap, null, revision, null, + Collections.singletonList(wire), Collections.EMPTY_MAP, Collections.EMPTY_MAP)); } - AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() + catch (Exception ex) + { + m_logger.log(revision.getBundle(), Logger.LOG_ERROR, + "Error resolving extension bundle : " + revision.getBundle(), ex); + } + + felix.getDependencies().addDependent(wire); + + appendCapabilities(entry.getKey().getDeclaredExtensionCapabilities(null)); + for (BundleWire w : entry.getValue()) { - @Override - public Void run() throws Exception + if (!w.getRequirement().getNamespace().equals(BundleRevision.HOST_NAMESPACE) && + !w.getRequirement().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)) { - extender.add(file); - return null; + ((BundleWiringImpl) w.getRequirer().getWiring()).addDynamicWire(w); + felix.getDependencies().addDependent(w); } - }); - if (exports != null) + } + + final File f; + Content revisionContent = revision.getContent(); + if (revisionContent instanceof JarContent) + { + f = ((JarContent) revisionContent).getFile(); + } + else { - appendCapabilities(exports); + f = ((DirectoryContent) revisionContent).getFile(); } + try + { + AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() + { + @Override + public Void run() throws Exception { + m_extenderFramework.add(f); + return null; + } + }); + } + catch (Exception ex) + { + m_logger.log(revision.getBundle(), Logger.LOG_ERROR, + "Error adding extension bundle to framework classloader: " + revision.getBundle(), ex); + } + + felix.setBundleStateAndNotify(revision.getBundle(), Bundle.RESOLVED); + result.add(revision.getBundle()); } - catch (Exception ex) + + if (!wirings.isEmpty()) { - throw ex; + felix.getResolver().addRevision(getRevision()); } - BundleRevisionImpl bri = bundle.adapt(BundleRevisionImpl.class); - List<BundleRequirement> reqs = bri.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE); - List<BundleCapability> caps = getCapabilities(BundleRevision.HOST_NAMESPACE); - BundleWire bw = new BundleWireImpl(bri, reqs.get(0), m_systemBundleRevision, caps.get(0)); - bri.resolve( - new BundleWiringImpl( - m_logger, - m_configMap, - null, - bri, - null, - Collections.singletonList(bw), - Collections.EMPTY_MAP, - Collections.EMPTY_MAP)); - felix.getDependencies().addDependent(bw); - felix.setBundleStateAndNotify(bundle, Bundle.RESOLVED); + return result; } /** - * This is a Felix specific extension mechanism that allows extension bundles - * to have activators and be started via this method. + * Start extension bundles that hasve an activator * * @param felix the framework instance the extension bundle is installed in. - * @param bundle the extension bundle to start if it has a Felix specific activator. + * @param bundle the extension bundle to start if it has a an extension bundle activator. */ void startExtensionBundle(Felix felix, BundleImpl bundle) { @@ -553,7 +560,7 @@ class ExtensionManager implements Conten { // TODO: SECURITY - Should this consider security? BundleActivator activator = (BundleActivator) - felix.getClass().getClassLoader().loadClass( + Felix.m_secureAction.getClassLoader(felix.getClass()).loadClass( activatorClass.trim()).newInstance(); BundleContext context = felix._getBundleContext(); @@ -643,6 +650,85 @@ class ExtensionManager implements Conten m_extensionTuples.clear(); } + private Map<BundleRevisionImpl, List<BundleWire>> findResolvableExtensions(List<BundleRevisionImpl> extensions) + { + // The idea is to loop through the extensions and try to resolve all unresolved extension. If we can't resolve + // a given extension, we will call the method again with the extension in question removed. + Map<BundleRevisionImpl, List<BundleWire>> wires = new LinkedHashMap<BundleRevisionImpl, List<BundleWire>>(); + + for (BundleRevisionImpl bri : extensions) + { + // Ignore resolved extensions + if (bri.getWiring() == null) + { + List<BundleWire> wi = new ArrayList<BundleWire>(); + boolean resolved = true; + outer: for (BundleRequirement req : bri.getDeclaredRequirements(null)) + { + // first see if we can resolve from the system bundle + for (BundleCapability cap : getCapabilities(req.getNamespace())) + { + if (req.matches(cap)) + { + // we can, create the wire but in the case of an ee requirement, make it from the extension + wi.add(new BundleWireImpl( + req.getNamespace().equals(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE) ? + bri : m_systemBundleRevision, req, m_systemBundleRevision, cap)); + + continue outer; + } + } + + // now loop through the other extensions + for (BundleRevisionImpl extension : extensions) + { + // check the caps that will be lifted to the system bundle + for (BundleCapability cap : extension.getDeclaredExtensionCapabilities(req.getNamespace())) + { + if (req.matches(cap)) + { + // we could use a yet unresolved extension (resolved one are implicitly checked by the + // system bundle loop above as they would be attached. + wi.add(new BundleWireImpl(m_systemBundleRevision, req, m_systemBundleRevision, cap)); + continue outer; + } + } + + // now check the caps that will not be lifted (i.e., identity) + for (BundleCapability cap : extension.getDeclaredCapabilities(req.getNamespace())) + { + if (req.matches(cap)) + { + // it was identity - hence, use the extension itself as provider + wi.add(new BundleWireImpl(m_systemBundleRevision, req, extension, cap)); + continue outer; + } + } + } + // we couldn't find a provider - was it optional? + if (!((BundleRequirementImpl)req).isOptional()) + { + resolved = false; + break; + } + } + if(resolved) + { + wires.put(bri, wi); + } + else + { + // we failed to resolve this extension - try again without it. Yes, this throws away the work done + // up to this point + List<BundleRevisionImpl> next = new ArrayList<BundleRevisionImpl>(extensions); + next.remove(bri); + return next.isEmpty() ? Collections.EMPTY_MAP : findResolvableExtensions(next); + } + } + } + return wires; + } + private List<BundleCapability> getCapabilities(String namespace) { List<BundleCapability> caps = m_capabilities; @@ -663,8 +749,7 @@ class ExtensionManager implements Conten private synchronized void appendCapabilities(List<BundleCapability> caps) { - List<BundleCapability> newCaps = - new ArrayList<BundleCapability>(m_capabilities.size() + caps.size()); + List<BundleCapability> newCaps = new ArrayList<BundleCapability>(m_capabilities.size() + caps.size()); newCaps.addAll(m_capabilities); newCaps.addAll(caps); m_capabilities = ImmutableList.newInstance(newCaps); Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/Felix.java URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=1826988&r1=1826987&r2=1826988&view=diff ============================================================================== --- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/Felix.java (original) +++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/Felix.java Fri Mar 16 13:44:36 2018 @@ -821,6 +821,11 @@ public class Felix extends BundleImpl im } } + for (Bundle extension : m_extensionManager.resolveExtensionBundles(this)) + { + m_extensionManager.startExtensionBundle(this, (BundleImpl) extension); + } + // Now that we have loaded all cached bundles and have determined the // max bundle ID of cached bundles, we need to try to load the next // bundle ID from persistent storage. In case of failure, we should @@ -1278,6 +1283,52 @@ public class Felix extends BundleImpl im }).start(); } + private void stopRefresh() throws BundleException + { + Object sm = System.getSecurityManager(); + + if (sm != null) + { + ((SecurityManager) sm).checkPermission(new AdminPermission(this, + AdminPermission.EXECUTE)); + } + + + // Stop the framework on a separate thread. + new Thread(new Runnable() { + @Override + public void run() + { + try + { + // First acquire the system bundle lock to verify the state. + acquireBundleLock(Felix.this, Bundle.STARTING | Bundle.ACTIVE); + // Set the reason for the shutdown. + m_shutdownGate.setMessage( + new FrameworkEvent(FrameworkEvent.STOPPED_SYSTEM_REFRESHED, Felix.this, null)); + // Record the state and stop the system bundle. + int oldState = Felix.this.getState(); + try + { + stop(); + } + catch (BundleException ex) + { + m_logger.log(Logger.LOG_WARNING, "Exception stopping framework.", ex); + } + finally + { + releaseBundleLock(Felix.this); + } + } + catch (Exception ex) + { + m_logger.log(Logger.LOG_WARNING, "Cannot update an inactive framework."); + } + } + }).start(); + } + @Override public String toString() { @@ -2494,10 +2545,6 @@ public class Felix extends BundleImpl im if (!wasExtension && bundle.isExtension()) { m_extensionManager.addExtensionBundle(this, bundle); -// TODO: REFACTOR - Perhaps we could move this into extension manager. - m_resolver.addRevision(m_extensionManager.getRevision()); -// TODO: REFACTOR - Not clear why this is here. We should look at all of these steps more closely. - setBundleStateAndNotify(bundle, Bundle.RESOLVED); } else if (wasExtension) { @@ -2542,9 +2589,10 @@ public class Felix extends BundleImpl im { setBundleStateAndNotify(bundle, Bundle.INSTALLED); } - else + + for (Bundle extension : m_extensionManager.resolveExtensionBundles(this)) { - m_extensionManager.startExtensionBundle(this, bundle); + m_extensionManager.startExtensionBundle(this, (BundleImpl) extension); } fireBundleEvent(BundleEvent.UNRESOLVED, bundle); @@ -3089,7 +3137,6 @@ public class Felix extends BundleImpl im if (bundle.isExtension()) { m_extensionManager.addExtensionBundle(this, bundle); - m_resolver.addRevision(m_extensionManager.getRevision()); } // Use a copy-on-write approach to add the bundle @@ -3124,11 +3171,6 @@ public class Felix extends BundleImpl im } } - if (bundle.isExtension()) - { - m_extensionManager.startExtensionBundle(this, bundle); - } - return bundle; } @@ -3215,7 +3257,6 @@ public class Felix extends BundleImpl im else { m_extensionManager.addExtensionBundle(this, bundle); - m_resolver.addRevision(m_extensionManager.getRevision()); } } catch (Throwable ex) @@ -3278,9 +3319,9 @@ public class Felix extends BundleImpl im releaseGlobalLock(); } - if (bundle.isExtension()) + for (Bundle extension : m_extensionManager.resolveExtensionBundles(this)) { - m_extensionManager.startExtensionBundle(this, bundle); + m_extensionManager.startExtensionBundle(this, (BundleImpl) extension); } } } @@ -4381,6 +4422,7 @@ public class Felix extends BundleImpl im try { boolean restart = false; + boolean extensionBundle = false; Bundle systemBundle = this; @@ -4391,9 +4433,14 @@ public class Felix extends BundleImpl im { for (Bundle b : bundles) { - if ((systemBundle == b) || ((BundleImpl) b).isExtension()) + if (systemBundle == b) + { + restart = true; + } + else if (((BundleImpl) b).isExtension()) { restart = true; + extensionBundle = true; break; } } @@ -4450,13 +4497,25 @@ public class Felix extends BundleImpl im } else { - try + if (!extensionBundle) { - update(); + try + { + update(); + } + catch (BundleException ex) { + m_logger.log(Logger.LOG_ERROR, "Framework restart error.", ex); + } } - catch (BundleException ex) + else { - m_logger.log(Logger.LOG_ERROR, "Framework restart error.", ex); + try + { + stopRefresh(); + } + catch (BundleException ex) { + m_logger.log(Logger.LOG_ERROR, "Framework stop error.", ex); + } } } } @@ -5147,6 +5206,7 @@ public class Felix extends BundleImpl im m_securityManager = null; } + m_extensionManager.removeExtensionBundles(Felix.this); m_dependencies.removeDependents(adapt(BundleRevision.class)); // Dispose of the bundle cache. Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java?rev=1826988&r1=1826987&r2=1826988&view=diff ============================================================================== --- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java (original) +++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java Fri Mar 16 13:44:36 2018 @@ -18,16 +18,6 @@ */ package org.apache.felix.framework.util.manifestparser; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import org.apache.felix.framework.BundleRevisionImpl; import org.apache.felix.framework.Logger; import org.apache.felix.framework.capabilityset.SimpleFilter; @@ -46,6 +36,16 @@ import org.osgi.framework.wiring.BundleC import org.osgi.framework.wiring.BundleRequirement; import org.osgi.framework.wiring.BundleRevision; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + public class ManifestParser { private static final String BUNDLE_LICENSE_HEADER = "Bundle-License"; // No constant defined by OSGi... @@ -60,6 +60,7 @@ public class ManifestParser private volatile String m_bundleSymbolicName; private volatile Version m_bundleVersion; private volatile List<BundleCapability> m_capabilities; + private volatile List<BundleCapability> m_extensionCapabilities; private volatile List<BundleRequirement> m_requirements; private volatile List<NativeLibraryClause> m_libraryClauses; private volatile boolean m_libraryHeadersOptional = false; @@ -157,6 +158,8 @@ public class ManifestParser "R4 bundle manifests must include bundle symbolic name."); } + m_isExtension = checkExtensionBundle(headerMap); + // // Parse Fragment-Host. // @@ -222,7 +225,7 @@ public class ManifestParser List<ParsedHeaderClause> provideClauses = parseStandardHeader((String) headerMap.get(Constants.PROVIDE_CAPABILITY)); - exportClauses = normalizeCapabilityClauses( + provideClauses = normalizeCapabilityClauses( logger, provideClauses, getManifestVersion()); List<BundleCapability> provideCaps = convertProvideCapabilities(provideClauses, owner); @@ -281,8 +284,19 @@ public class ManifestParser m_capabilities = new ArrayList<BundleCapability>( capList.size() + exportCaps.size() + provideCaps.size()); m_capabilities.addAll(capList); - m_capabilities.addAll(exportCaps); - m_capabilities.addAll(provideCaps); + + if (m_isExtension) + { + m_extensionCapabilities = new ArrayList<BundleCapability>(); + m_extensionCapabilities.addAll(exportCaps); + m_extensionCapabilities.addAll(provideCaps); + } + else + { + m_extensionCapabilities = Collections.EMPTY_LIST; + m_capabilities.addAll(exportCaps); + m_capabilities.addAll(provideCaps); + } // // Parse activation policy. @@ -291,8 +305,6 @@ public class ManifestParser // This sets m_activationPolicy, m_includedPolicyClasses, and // m_excludedPolicyClasses. parseActivationPolicy(headerMap); - - m_isExtension = checkExtensionBundle(headerMap); } private static List<ParsedHeaderClause> normalizeImportClauses( @@ -1038,11 +1050,6 @@ public class ManifestParser return m_requirements; } - public List<NativeLibraryClause> getLibraryClauses() - { - return m_libraryClauses; - } - /** * <p> * This method returns the selected native library metadata from @@ -1351,8 +1358,7 @@ public class ManifestParser throw new BundleException( "Extension bundle must have either 'extension:=framework' or 'extension:=bootclasspath'"); } - if (headerMap.containsKey(Constants.IMPORT_PACKAGE) || - headerMap.containsKey(Constants.REQUIRE_BUNDLE) || + if (headerMap.containsKey(Constants.REQUIRE_BUNDLE) || headerMap.containsKey(Constants.BUNDLE_NATIVECODE) || headerMap.containsKey(Constants.DYNAMICIMPORT_PACKAGE) || headerMap.containsKey(Constants.BUNDLE_ACTIVATOR)) @@ -1555,24 +1561,6 @@ public class ManifestParser return reqs; } - public static List<BundleCapability> parseExportHeader( - Logger logger, BundleRevision owner, String header, String bsn, Version bv) - { - - List<BundleCapability> caps = null; - try - { - List<ParsedHeaderClause> exportClauses = parseStandardHeader(header); - exportClauses = normalizeExportClauses(logger, exportClauses, "2", bsn, bv); - caps = convertExports(exportClauses, owner); - } - catch (BundleException ex) - { - caps = null; - } - return caps; - } - private static List<BundleRequirement> parseBreeHeader(String header, BundleRevision owner) { List<String> filters = new ArrayList<String>(); @@ -1686,7 +1674,8 @@ public class ManifestParser } } - private static String getBreeVersionClause(Version ver) { + private static String getBreeVersionClause(Version ver) + { if (ver == null) return null; @@ -2150,4 +2139,71 @@ public class ManifestParser return libList; } + + public static List<BundleCapability> aliasSymbolicName(List<BundleCapability> caps, BundleRevision owner) + { + if (caps == null) + { + return new ArrayList<BundleCapability>(0); + } + + List<BundleCapability> aliasCaps = new ArrayList<BundleCapability>(caps); + + String[] aliases = { + FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME, + Constants.SYSTEM_BUNDLE_SYMBOLICNAME }; + + for (int capIdx = 0; capIdx < aliasCaps.size(); capIdx++) + { + BundleCapability cap = aliasCaps.get(capIdx); + + // Need to alias bundle and host capabilities. + if (cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) + || cap.getNamespace().equals(BundleRevision.HOST_NAMESPACE)) + { + // Make a copy of the attribute array. + Map<String, Object> aliasAttrs = + new HashMap<String, Object>(cap.getAttributes()); + // Add the aliased value. + aliasAttrs.put(cap.getNamespace(), aliases); + // Create the aliased capability to replace the old capability. + cap = new BundleCapabilityImpl( + owner, + cap.getNamespace(), + cap.getDirectives(), + aliasAttrs); + aliasCaps.set(capIdx, cap); + } + + // Further, search attributes for bundle symbolic name and alias it too. + for (Entry<String, Object> entry : cap.getAttributes().entrySet()) + { + // If there is a bundle symbolic name attribute, add the + // standard alias as a value. + if (entry.getKey().equalsIgnoreCase(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) + { + // Make a copy of the attribute array. + Map<String, Object> aliasAttrs = + new HashMap<String, Object>(cap.getAttributes()); + // Add the aliased value. + aliasAttrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, aliases); + // Create the aliased capability to replace the old capability. + aliasCaps.set(capIdx, new BundleCapabilityImpl( + owner, + cap.getNamespace(), + cap.getDirectives(), + aliasAttrs)); + // Continue with the next capability. + break; + } + } + } + + return aliasCaps; + } + + public List<BundleCapability> getExtensionCapabilites() + { + return m_extensionCapabilities; + } }