From a731195ec06070c34a03d7bfd15b51bfd1fd5283 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 14 Nov 2019 11:01:29 -0600 Subject: Bug 552594 - Fix framework extensions with built-in Java 9 app loader In Java 9 the application class loader changed from a URLClassLoader to an internal type jdk.internal.loader.ClassLoaders.AppClassLoader. The new AppClassLoader no longer has the addURL method that we used to add framework extension content to the class loader when the framework is loaded from the java class path. This fix uses the method appendToClassPathForInstrumentation(String) on the new AppClassLoader class. To do this extra work is needed to allow reflection on the AppClassLoader class. This is not ideal, but will allow the framework extensions to work when the framework is launched from the java application class path. Note that Eclipse launcher uses its own class loader which still has addURL method available. This new approach is only attempted if the class loader used to launch the framework does not have an available addURL method. Change-Id: I0a4195307db6fb87dbab08933584e1a798ccf34a Signed-off-by: Thomas Watson --- .../osgi/internal/url/MultiplexingFactory.java | 2 +- .../osgi/storage/FrameworkExtensionInstaller.java | 65 +++++++++++++++------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/url/MultiplexingFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/url/MultiplexingFactory.java index 5d9ced4b3..ec38d9bed 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/url/MultiplexingFactory.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/url/MultiplexingFactory.java @@ -49,7 +49,7 @@ public abstract class MultiplexingFactory { * The setAccessible class will be defined in the java.base module which grants * it the ability to call setAccessible(true) on other types from the java.base module */ - static final Collection setAccessible; + public static final Collection setAccessible; static final Collection systemLoaders; static { Collection result = null; diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/FrameworkExtensionInstaller.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/FrameworkExtensionInstaller.java index 939e9548f..59f570185 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/FrameworkExtensionInstaller.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/FrameworkExtensionInstaller.java @@ -14,6 +14,7 @@ package org.eclipse.osgi.storage; import java.io.File; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; @@ -36,6 +37,7 @@ import org.eclipse.osgi.internal.framework.EquinoxConfiguration; import org.eclipse.osgi.internal.hookregistry.ActivatorHookFactory; import org.eclipse.osgi.internal.hookregistry.HookRegistry; import org.eclipse.osgi.internal.messages.Msg; +import org.eclipse.osgi.internal.url.MultiplexingFactory; import org.eclipse.osgi.storage.BundleInfo.Generation; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; @@ -50,29 +52,40 @@ import org.osgi.resource.Capability; public class FrameworkExtensionInstaller { private static final ClassLoader CL = FrameworkExtensionInstaller.class.getClassLoader(); private static final Method ADD_FWK_URL_METHOD = findAddURLMethod(CL, "addURL"); //$NON-NLS-1$ + private static final Method ADD_FWK_FILE_PATH_METHOD = ADD_FWK_URL_METHOD == null ? findAddFilePathMethod(CL, "appendToClassPathForInstrumentation") : null; //$NON-NLS-1$ private final ArrayMap hookActivators = new ArrayMap<>(5); private static Method findAddURLMethod(ClassLoader cl, String name) { if (cl == null) return null; - return findMethod(cl.getClass(), name, new Class[] {URL.class}); + return findMethod(cl.getClass(), name, new Class[] {URL.class}, null); + } + + private static Method findAddFilePathMethod(ClassLoader cl, String name) { + if (cl == null) + return null; + return findMethod(cl.getClass(), name, new Class[] {String.class}, MultiplexingFactory.setAccessible); } // recursively searches a class and it's superclasses for a (potentially inaccessable) method - private static Method findMethod(Class clazz, String name, Class[] args) { + private static Method findMethod(Class clazz, String name, Class[] args, Collection setAccessible) { if (clazz == null) return null; // ends the recursion when getSuperClass returns null try { Method result = clazz.getDeclaredMethod(name, args); - result.setAccessible(true); + if (setAccessible != null) { + setAccessible.add(result); + } else { + result.setAccessible(true); + } return result; } catch (SecurityException e) { // if we do not have the permissions then we will not find the method } catch (NoSuchMethodException | RuntimeException e) { // do nothing look in super class below // have to avoid blowing up - } - return findMethod(clazz.getSuperclass(), name, args); + } + return findMethod(clazz.getSuperclass(), name, args, setAccessible); } private static void callAddURLMethod(URL arg) throws InvocationTargetException { @@ -83,6 +96,14 @@ public class FrameworkExtensionInstaller { } } + private static void callAddFilePathMethod(File file) throws InvocationTargetException { + try { + ADD_FWK_FILE_PATH_METHOD.invoke(CL, new Object[] {file.getCanonicalPath()}); + } catch (Throwable t) { + throw new InvocationTargetException(t); + } + } + private final EquinoxConfiguration configuration; public FrameworkExtensionInstaller(EquinoxConfiguration configuraiton) { @@ -113,34 +134,36 @@ public class FrameworkExtensionInstaller { // framework extensions return; } - if (CL == null || ADD_FWK_URL_METHOD == null) { - // use the first revision as the blame - ModuleRevision revision = revisions.iterator().next(); - throw new BundleException("Cannot support framework extension bundles without a public addURL(URL) method on the framework class loader: " + revision.getBundle()); //$NON-NLS-1$ - } for (ModuleRevision revision : revisions) { - File[] files = getExtensionFiles(revision); - if (files == null) { - return; + if (CL == null || (ADD_FWK_URL_METHOD == null && ADD_FWK_FILE_PATH_METHOD == null)) { + // use the first revision as the blame + throw new BundleException("Cannot support framework extension bundles without a public addURL(URL) or appendToClassPathForInstrumentation(String) method on the framework class loader: " + revision.getBundle()); //$NON-NLS-1$ } + File[] files = getExtensionFiles(revision); for (File file : files) { if (file == null) { - continue; + continue; } try { - callAddURLMethod(StorageUtil.encodeFileURL(file)); - }catch (InvocationTargetException | MalformedURLException e) { + if (ADD_FWK_URL_METHOD != null) { + callAddURLMethod(StorageUtil.encodeFileURL(file)); + } else if (ADD_FWK_FILE_PATH_METHOD != null) { + callAddFilePathMethod(file); + } + } catch (InvocationTargetException | MalformedURLException e) { throw new BundleException("Error adding extension content.", e); //$NON-NLS-1$ } } } - try { - // initialize the new urls - CL.loadClass("thisIsNotAClass"); //$NON-NLS-1$ - } catch (ClassNotFoundException e) { - // do nothing + if (CL != null) { + try { + // initialize the new urls + CL.loadClass("thisIsNotAClass"); //$NON-NLS-1$ + } catch (ClassNotFoundException e) { + // do nothing + } } if (systemModule != null) { BundleContext systemContext = systemModule.getBundle().getBundleContext(); -- cgit v1.2.3