You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2017/10/23 23:06:46 UTC
svn commit: r1813111 - in
/felix/trunk/framework/src/main/java/org/apache/felix/framework:
ExtensionManager.java Felix.java cache/DirectoryContent.java
ext/ClassPathExtenderFactory.java
Author: pauls
Date: Mon Oct 23 23:06:46 2017
New Revision: 1813111
URL: http://svn.apache.org/viewvc?rev=1813111&view=rev
Log:
FELIX-5727: Refactor extension bundle support to allow for custom classloader appenders and to add extension bundles when loaded with java9 PlattformClassLoader.
Added:
felix/trunk/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
Modified:
felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java?rev=1813111&r1=1813110&r2=1813111&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java Mon Oct 23 23:06:46 2017
@@ -18,28 +18,10 @@
*/
package org.apache.felix.framework;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.security.AllPermission;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.Set;
-
import org.apache.felix.framework.cache.Content;
+import org.apache.felix.framework.cache.DirectoryContent;
+import org.apache.felix.framework.cache.JarContent;
+import org.apache.felix.framework.ext.ClassPathExtenderFactory;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.ImmutableList;
import org.apache.felix.framework.util.StringMap;
@@ -64,6 +46,27 @@ import org.osgi.framework.wiring.BundleR
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.Set;
+
/**
* The ExtensionManager class is used in several ways.
* <p>
@@ -87,49 +90,46 @@ import org.osgi.framework.wiring.BundleW
// with the parent classloader and one instance per framework instance that
// keeps track of extension bundles and systembundle exports for that framework
// instance.
-class ExtensionManager extends URLStreamHandler implements Content
+class ExtensionManager implements Content
{
- // The private instance that is added to Felix.class.getClassLoader() -
- // will be null if extension bundles are not supported (i.e., we are not
- // loaded by an instance of URLClassLoader)
- static final ExtensionManager m_extensionManager;
+ static final ClassPathExtenderFactory.ClassPathExtender m_extenderFramework;
static
{
- // pre-init the url sub-system as otherwise we don't work on gnu/classpath
- ExtensionManager extensionManager = null;
+ ClassPathExtenderFactory.ClassPathExtender extenderFramework = null;
- if (!"true".equalsIgnoreCase(Felix.m_secureAction.getSystemProperty(
- FelixConstants.FELIX_EXTENSIONS_DISABLE, "false")))
+ if (!"true".equalsIgnoreCase(Felix.m_secureAction.getSystemProperty(FelixConstants.FELIX_EXTENSIONS_DISABLE, "false")))
{
- try
- {
- (new URL("http://felix.extensions:9/")).openConnection();
- }
- catch (Throwable t)
+ ServiceLoader<ClassPathExtenderFactory> loader = ServiceLoader.load(ClassPathExtenderFactory.class,
+ ExtensionManager.class.getClassLoader());
+
+
+ for (Iterator<ClassPathExtenderFactory> iter = loader.iterator(); iter.hasNext() && extenderFramework == null; )
{
- // This doesn't matter much - we only need the above to init the url subsystem
+ try
+ {
+ extenderFramework = iter.next().getExtender(ExtensionManager.class.getClassLoader());
+ }
+ catch (Throwable t)
+ {
+ // Ignore
+ }
}
- // We use the secure action of Felix to add a new instance to the parent
- // classloader.
try
{
- extensionManager = new ExtensionManager();
-
- Felix.m_secureAction.addURLToURLClassLoader(Felix.m_secureAction.createURL(
- Felix.m_secureAction.createURL(null, "http:", extensionManager),
- "http://felix.extensions:9/", extensionManager),
- Felix.class.getClassLoader());
+ if (extenderFramework == null)
+ {
+ extenderFramework = new ClassPathExtenderFactory.DefaultClassLoaderExtender()
+ .getExtender(ExtensionManager.class.getClassLoader());
+ }
}
- catch (Throwable ex)
- {
- // extension bundles will not be supported.
- extensionManager = null;
+ catch (Throwable t) {
+ // Ignore
}
}
- m_extensionManager = extensionManager;
+ m_extenderFramework = extenderFramework;
}
private final Logger m_logger;
@@ -139,10 +139,6 @@ class ExtensionManager extends URLStream
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 m_extensions;
- private volatile Bundle[] m_extensionsCache;
- private final Set m_names;
- private final Map m_sourceToExtensions;
private final List<ExtensionTuple> m_extensionTuples = Collections.synchronizedList(new ArrayList<ExtensionTuple>());
private static class ExtensionTuple
@@ -157,20 +153,6 @@ class ExtensionManager extends URLStream
m_activator = activator;
m_bundle = bundle;
}
-
- }
-
- // This constructor is only used for the private instance added to the parent
- // classloader.
- private ExtensionManager()
- {
- m_logger = null;
- m_configMap = null;
- m_systemBundleRevision = null;
- m_extensions = new ArrayList();
- m_extensionsCache = new Bundle[0];
- m_names = new HashSet();
- m_sourceToExtensions = new HashMap();
}
/**
@@ -181,18 +163,14 @@ class ExtensionManager extends URLStream
* instance.
*
* @param logger the logger to use.
- * @param config the configuration to read properties from.
- * @param systemBundleInfo the info to change if we need to add exports.
+ * @param configMap the configuration to read properties from.
+ * @param felix the framework.
*/
ExtensionManager(Logger logger, Map configMap, Felix felix)
{
m_logger = logger;
m_configMap = configMap;
m_systemBundleRevision = new ExtensionManagerRevision(felix);
- m_extensions = null;
- m_extensionsCache = null;
- m_names = null;
- m_sourceToExtensions = null;
// TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
// Populate system bundle header map.
@@ -415,49 +393,75 @@ class ExtensionManager extends URLStream
// We only support classpath extensions (not bootclasspath).
if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
{
- throw new BundleException("Unsupported Extension Bundle type: " +
- directive, new UnsupportedOperationException(
- "Unsupported Extension Bundle type!"));
+ throw new BundleException("Unsupported Extension Bundle type: " +
+ directive, new UnsupportedOperationException(
+ "Unsupported Extension Bundle type!"));
+ }
+
+ 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)?");
+
+ throw new UnsupportedOperationException(
+ "Unable to add extension bundle.");
+ }
+
+ Content content = bundle.adapt(BundleRevisionImpl.class).getContent();
+ final File file;
+ if (content instanceof JarContent)
+ {
+ file = ((JarContent) content).getFile();
}
+ else if (content instanceof DirectoryContent)
+ {
+ file = ((DirectoryContent) content).getFile();
+ }
+ else
+ {
+ file = null;
+ }
+ if (file == null)
+ {
+ // We don't support revision type for extension
+ m_logger.log(bundle, Logger.LOG_WARNING,
+ "Unable to add extension bundle - wrong revision type?");
+ throw new UnsupportedOperationException(
+ "Unable to add extension bundle.");
+ }
try
{
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ m_extenderFramework.add(file);
+ return null;
+ }
+ });
+
// Merge the exported packages with the exported packages of the systembundle.
List<BundleCapability> exports = null;
try
{
exports = ManifestParser.parseExportHeader(
- m_logger, m_systemBundleRevision,
- (String) ((BundleRevisionImpl) bundle.adapt(BundleRevisionImpl.class))
- .getHeaders().get(Constants.EXPORT_PACKAGE),
- m_systemBundleRevision.getSymbolicName(), m_systemBundleRevision.getVersion());
+ m_logger, m_systemBundleRevision,
+ (String) bundle.adapt(BundleRevisionImpl.class).getHeaders().get(Constants.EXPORT_PACKAGE),
+ m_systemBundleRevision.getSymbolicName(), m_systemBundleRevision.getVersion());
exports = aliasSymbolicName(exports);
}
catch (Exception ex)
{
m_logger.log(
- bundle,
- Logger.LOG_ERROR,
- "Error parsing extension bundle export statement: "
- + ((BundleRevisionImpl) bundle.adapt(BundleRevisionImpl.class))
- .getHeaders().get(Constants.EXPORT_PACKAGE), ex);
+ bundle,
+ Logger.LOG_ERROR,
+ "Error parsing extension bundle export statement: "
+ + bundle.adapt(BundleRevisionImpl.class).getHeaders().get(Constants.EXPORT_PACKAGE), ex);
return;
}
-
- // Add the bundle as extension if we support extensions
- if (m_extensionManager != null)
- {
- // This needs to be the private instance.
- m_extensionManager.addExtension(felix, bundle);
- }
- else
- {
- // We don't support extensions (i.e., the parent is not an URLClassLoader).
- m_logger.log(bundle, Logger.LOG_WARNING,
- "Unable to add extension bundle to FrameworkClassLoader - Maybe not an URLClassLoader?");
- throw new UnsupportedOperationException(
- "Unable to add extension bundle to FrameworkClassLoader - Maybe not an URLClassLoader?");
- }
appendCapabilities(exports);
}
catch (Exception ex)
@@ -465,7 +469,7 @@ class ExtensionManager extends URLStream
throw ex;
}
- BundleRevisionImpl bri = (BundleRevisionImpl) bundle.adapt(BundleRevisionImpl.class);
+ 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));
@@ -598,21 +602,6 @@ class ExtensionManager extends URLStream
m_extensionTuples.clear();
}
- /**
- * Remove all extension registered by the given framework instance. Note, it
- * is not possible to unregister allready loaded classes form those extensions.
- * That is why the spec requires a JVM restart.
- *
- * @param felix the framework instance whose extensions need to be unregistered.
- */
- void removeExtensions(Felix felix)
- {
- if (m_extensionManager != null)
- {
- m_extensionManager._removeExtensions(felix);
- }
- }
-
private List<BundleCapability> getCapabilities(String namespace)
{
List<BundleCapability> caps = m_capabilities;
@@ -692,111 +681,6 @@ class ExtensionManager extends URLStream
return exportSB.toString();
}
- //
- // Classpath Extension
- //
-
- /*
- * See whether any registered extension provides the class requested. If not
- * throw an IOException.
- */
- public URLConnection openConnection(URL url) throws IOException
- {
- String path = url.getPath();
-
- if (path.trim().equals("/"))
- {
- return new URLHandlersBundleURLConnection(url);
- }
-
- Bundle[] extensions = m_extensionsCache;
- URL result = null;
- for (Bundle extBundle : extensions)
- {
- try
- {
- BundleRevisionImpl bri =
- (BundleRevisionImpl) extBundle.adapt(BundleRevision.class);
- if (bri != null)
- {
- result = bri.getResourceLocal(path);
- }
- }
- catch (Exception ex)
- {
- // Maybe the bundle went away, so ignore this exception.
- }
- if (result != null)
- {
- return result.openConnection();
- }
- }
-
- return new URLConnection(url)
- {
- public void connect() throws IOException
- {
- throw new IOException("Resource not provided by any extension!");
- }
- };
- }
-
- @Override
- protected InetAddress getHostAddress(URL u)
- {
- // the extension URLs do not address real hosts
- return null;
- }
-
- private synchronized void addExtension(Object source, Bundle extension)
- {
- List sourceExtensions = (List) m_sourceToExtensions.get(source);
-
- if (sourceExtensions == null)
- {
- sourceExtensions = new ArrayList();
- m_sourceToExtensions.put(source, sourceExtensions);
- }
-
- sourceExtensions.add(extension);
-
- _add(extension.getSymbolicName(), extension);
- m_extensionsCache = (Bundle[])
- m_extensions.toArray(new Bundle[m_extensions.size()]);
- }
-
- private synchronized void _removeExtensions(Object source)
- {
- if (m_sourceToExtensions.remove(source) == null)
- {
- return;
- }
-
- m_extensions.clear();
- m_names.clear();
-
- for (Iterator iter = m_sourceToExtensions.values().iterator(); iter.hasNext();)
- {
- List extensions = (List) iter.next();
- for (Iterator extIter = extensions.iterator(); extIter.hasNext();)
- {
- Bundle bundle = (Bundle) extIter.next();
- _add(bundle.getSymbolicName(), bundle);
- }
- m_extensionsCache = (Bundle[])
- m_extensions.toArray(new Bundle[m_extensions.size()]);
- }
- }
-
- private void _add(String name, Bundle extension)
- {
- if (!m_names.contains(name))
- {
- m_names.add(name);
- m_extensions.add(extension);
- }
- }
-
public void close()
{
// Do nothing on close, since we have nothing open.
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=1813111&r1=1813110&r2=1813111&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Mon Oct 23 23:06:46 2017
@@ -4990,11 +4990,6 @@ public class Felix extends BundleImpl im
m_dependencies.removeDependents(adapt(BundleRevision.class));
- if (m_extensionManager != null)
- {
- m_extensionManager.removeExtensions(Felix.this);
- }
-
// Dispose of the bundle cache.
m_cache.release();
m_cache = null;
Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java?rev=1813111&r1=1813110&r2=1813111&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java Mon Oct 23 23:06:46 2017
@@ -53,6 +53,11 @@ public class DirectoryContent implements
m_dir = dir;
}
+ public File getFile()
+ {
+ return m_dir;
+ }
+
public void close()
{
// Nothing to clean up.
Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java?rev=1813111&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java Mon Oct 23 23:06:46 2017
@@ -0,0 +1,153 @@
+/*
+ * 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.ext;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public interface ClassPathExtenderFactory
+{
+ interface ClassPathExtender
+ {
+ void add(File file) throws Exception;
+ }
+
+ ClassPathExtender getExtender(ClassLoader loader);
+
+ final class DefaultClassLoaderExtender implements ClassPathExtenderFactory, ClassPathExtenderFactory.ClassPathExtender
+ {
+ private static final Method m_append;
+ private static final ClassLoader m_app;
+ private static final Method m_addURL;
+ private final ClassLoader m_loader;
+
+ static
+ {
+ ClassLoader app = ClassLoader.getSystemClassLoader();
+
+ Method append = null;
+
+ while (app != null)
+ {
+ try
+ {
+ append = app.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
+ append.setAccessible(true);
+ break;
+ }
+ catch (Exception e)
+ {
+ append = null;
+ try
+ {
+ app = app.getParent();
+ }
+ catch (Exception ex)
+ {
+ app = null;
+ }
+ }
+ }
+ m_append = append;
+ m_app = app;
+
+ Method addURL;
+
+ try
+ {
+ addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+ addURL.setAccessible(true);
+ }
+ catch (Exception e)
+ {
+ addURL = null;
+ }
+
+ m_addURL = addURL;
+ }
+
+ public DefaultClassLoaderExtender()
+ {
+ this(null);
+ }
+
+ private DefaultClassLoaderExtender(ClassLoader loader)
+ {
+ m_loader = loader;
+ }
+
+ @Override
+ public ClassPathExtender getExtender(ClassLoader loader)
+ {
+ if (m_append != null)
+ {
+ ClassLoader current = ClassLoader.getSystemClassLoader();
+
+ while (current != null)
+ {
+ if (loader == current)
+ {
+ return this;
+ }
+ current = current.getParent();
+ }
+ }
+ if (m_addURL != null && loader instanceof URLClassLoader)
+ {
+ return new DefaultClassLoaderExtender(loader);
+ }
+ return null;
+ }
+
+ @Override
+ public void add(final File file) throws Exception
+ {
+ ClassLoader loader;
+ if (m_loader != null)
+ {
+ loader = m_loader;
+ synchronized (m_loader)
+ {
+ m_addURL.invoke(m_loader, file.getCanonicalFile().toURI().toURL());
+ }
+ }
+ else
+ {
+ loader = m_app;
+ synchronized (m_app)
+ {
+ m_append.invoke(m_app, file.getCanonicalFile().getPath());
+ }
+ }
+
+ try
+ {
+ for (int i = 0; i < 1000; i++)
+ {
+ loader.loadClass("flushFelixExtensionSubsystem" + i + ".class");
+ }
+ }
+ catch (Exception ex) {
+ // This is expected, we need to init the url subsystem
+ }
+ }
+ }
+}