# HG changeset patch # User attila # Date 1445376886 -7200 # Node ID 6c945c826f36024dd447844c41c78e081ae3b747 # Parent 334cd3ebfa5ea741ea0e5336db52021e8d146314 8139895: Introduce GuardingDynamicLinkerExporter Reviewed-by: hannesw, sundar diff -r 334cd3ebfa5e -r 6c945c826f36 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Tue Oct 20 23:34:16 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Tue Oct 20 23:34:46 2015 +0200 @@ -92,15 +92,19 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; +import java.util.function.Supplier; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardedInvocationTransformer; import jdk.internal.dynalink.linker.GuardingDynamicLinker; +import jdk.internal.dynalink.linker.GuardingDynamicLinkerExporter; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; @@ -141,6 +145,8 @@ private MethodTypeConversionStrategy autoConversionStrategy; private MethodHandleTransformer internalObjectsFilter; + private List autoLoadingErrors = Collections.emptyList(); + /** * Creates a new dynamic linker factory with default configuration. Upon * creation, the factory can be configured using various {@code setXxx()} @@ -152,17 +158,18 @@ /** * Sets the class loader for automatic discovery of available guarding - * dynamic linkers. Guarding dynamic linker classes declared in - * {@code /META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinker} - * resources available through this class loader will be automatically - * instantiated using the {@link ServiceLoader} mechanism and incorporated - * into {@code DynamicLinker}s that this factory creates. This allows for - * cross-language interoperability where call sites belonging to this language - * runtime can be linked by linkers from these autodiscovered runtimes if - * their native objects are passed to this runtime. If class loader is not - * set explicitly, then the thread context class loader of the thread - * invoking {@link #createLinker()} will be used. Can be invoked with null - * to signify system class loader. + * dynamic linkers. {@link GuardingDynamicLinkerExporter} implementations + * available through this class loader will be automatically instantiated + * using the {@link ServiceLoader} mechanism and the linkers they provide + * will be incorporated into {@code DynamicLinker}s that this factory + * creates. This allows for cross-language interoperability where call sites + * belonging to this language runtime can be linked by linkers from these + * automatically discovered runtimes if their native objects are passed to + * this runtime. If class loader is not set explicitly by invoking this + * method, then the thread context class loader of the thread invoking + * {@link #createLinker()} will be used. If this method is invoked + * explicitly with null then {@link ServiceLoader#loadInstalled(Class)} will + * be used to load the linkers. * * @param classLoader the class loader used for the automatic discovery of * available linkers. @@ -185,7 +192,7 @@ * @throws NullPointerException if any of the list elements are null. */ public void setPrioritizedLinkers(final List prioritizedLinkers) { - this.prioritizedLinkers = reqireNonNullElements(prioritizedLinkers); + this.prioritizedLinkers = copyListRequireNonNullElements(prioritizedLinkers); } /** @@ -227,7 +234,7 @@ * @throws NullPointerException if any of the list elements are null. */ public void setFallbackLinkers(final List fallbackLinkers) { - this.fallbackLinkers = reqireNonNullElements(fallbackLinkers); + this.fallbackLinkers = copyListRequireNonNullElements(fallbackLinkers); } /** @@ -345,9 +352,14 @@ /** * Creates a new dynamic linker based on the current configuration. This * method can be invoked more than once to create multiple dynamic linkers. - * Autodiscovered linkers are newly instantiated on every invocation of this - * method. It is allowed to change the factory's configuration between - * invocations. The method is not thread safe. + * Automatically discovered linkers are newly instantiated on every + * invocation of this method. It is allowed to change the factory's + * configuration between invocations. The method is not thread safe. After + * invocation, callers can invoke {@link #getAutoLoadingErrors()} to + * retrieve a list of {@link ServiceConfigurationError}s that occurred while + * trying to load automatically discovered linkers. These are never thrown + * from the call to this method as it makes every effort to recover from + * them and ignore the failing linkers. * @return the new dynamic Linker */ public DynamicLinker createLinker() { @@ -366,12 +378,7 @@ addClasses(knownLinkerClasses, prioritizedLinkers); addClasses(knownLinkerClasses, fallbackLinkers); - final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader(); - final List discovered = new LinkedList<>(); - final ServiceLoader linkerLoader = ServiceLoader.load(GuardingDynamicLinker.class, effectiveClassLoader); - for(final GuardingDynamicLinker linker: linkerLoader) { - discovered.add(linker); - } + final List discovered = discoverAutoLoadLinkers(); // Now, concatenate ... final List linkers = @@ -420,6 +427,68 @@ syncOnRelink, unstableRelinkThreshold); } + /** + * Returns a list of {@link ServiceConfigurationError}s that were + * encountered while loading automatically discovered linkers during the + * last invocation of {@link #createLinker()}. They can be any non-Dynalink + * specific service configuration issues, as well as some Dynalink-specific + * errors when an exporter that the factory tried to automatically load: + *
    + *
  • did not have the runtime permission named + * {@link GuardingDynamicLinkerExporter#AUTOLOAD_PERMISSION_NAME} in a + * system with a security manager, or
  • + *
  • returned null from {@link GuardingDynamicLinkerExporter#get()}, or
  • + *
  • the list returned from {@link GuardingDynamicLinkerExporter#get()} + * had a null element.
  • + *
+ * @return an immutable list of encountered + * {@link ServiceConfigurationError}s. Can be empty. + */ + public List getAutoLoadingErrors() { + return Collections.unmodifiableList(autoLoadingErrors); + } + + private List discoverAutoLoadLinkers() { + autoLoadingErrors = new LinkedList<>(); + final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader(); + final List discovered = new LinkedList<>(); + try { + final ServiceLoader linkerLoader = + AccessController.doPrivileged((PrivilegedAction>)()-> { + if (effectiveClassLoader == null) { + return ServiceLoader.loadInstalled(GuardingDynamicLinkerExporter.class); + } + return ServiceLoader.load(GuardingDynamicLinkerExporter.class, effectiveClassLoader); + }); + + for(final Iterator it = linkerLoader.iterator(); it.hasNext();) { + try { + final GuardingDynamicLinkerExporter autoLoader = it.next(); + try { + discovered.addAll(requireNonNullElements( + Objects.requireNonNull(autoLoader.get(), + ()->(autoLoader.getClass().getName() + " returned null from get()")), + ()->(autoLoader.getClass().getName() + " returned a list with at least one null element"))); + } catch (final ServiceConfigurationError|VirtualMachineError e) { + // Don't wrap a SCE in another SCE. Also, don't ignore + // any VME (e.g. StackOverflowError or OutOfMemoryError). + throw e; + } catch (final Throwable t) { + throw new ServiceConfigurationError(t.getMessage(), t); + } + } catch (final ServiceConfigurationError e) { + // Catch SCE with an individual exporter, carry on with it.hasNext(). + autoLoadingErrors.add(e); + } + } + } catch (final ServiceConfigurationError e) { + // Catch a top-level SCE; one either in ServiceLoader.load(), + // ServiceLoader.iterator(), or Iterator.hasNext(). + autoLoadingErrors.add(e); + } + return discovered; + } + private static ClassLoader getThreadContextClassLoader() { return AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -436,14 +505,18 @@ } } - private static List reqireNonNullElements(final List list) { + private static List copyListRequireNonNullElements(final List list) { if (list == null) { return null; } + return new ArrayList<>(requireNonNullElements(list, ()->"List has at least one null element")); + } + + private static List requireNonNullElements(final List list, final Supplier msgSupplier) { for(final T t: list) { - Objects.requireNonNull(t); + Objects.requireNonNull(t, msgSupplier); } - return new ArrayList<>(list); + return list; } } diff -r 334cd3ebfa5e -r 6c945c826f36 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java Tue Oct 20 23:34:16 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinker.java Tue Oct 20 23:34:46 2015 +0200 @@ -101,16 +101,15 @@ * set some as {@link DynamicLinkerFactory#setFallbackLinkers(List) fallback * linkers} to handle language-specific "property not found" etc. conditions. *

- * Languages can declare the linkers they want to expose to other runtimes for - * {@link DynamicLinkerFactory#setClassLoader(ClassLoader) automatic discovery} - * in META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinker - * resources of their JAR files. - *

* Consider implementing {@link TypeBasedGuardingDynamicLinker} interface * instead of this interface for those linkers that are based on the Java class * of the objects. If you need to implement language-specific type conversions, * have your {@code GuardingDynamicLinker} also implement the * {@link GuardingTypeConverterFactory} interface. + *

+ * Languages can export linkers to other language runtimes for + * {@link DynamicLinkerFactory#setClassLoader(ClassLoader) automatic discovery} + * using a {@link GuardingDynamicLinkerExporter}. */ public interface GuardingDynamicLinker { /** diff -r 334cd3ebfa5e -r 6c945c826f36 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinkerExporter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/GuardingDynamicLinkerExporter.java Tue Oct 20 23:34:46 2015 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.dynalink.linker; + +import java.security.Permission; +import java.util.List; +import java.util.ServiceLoader; +import java.util.function.Supplier; +import jdk.internal.dynalink.DynamicLinkerFactory; + +/** + * A class acting as a supplier of guarding dynamic linkers that can be + * automatically loaded by other language runtimes. Language runtimes wishing + * to export their own linkers should subclass this class and implement the + * {@link #get()} method to return a list of exported linkers and declare the + * subclass in + * {@code /META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinkerExporter} + * resource of their distribution (typically, JAR file) so that dynamic linker + * factories can discover them using the {@link ServiceLoader} mechanism. Note + * that instantiating this class is tied to a security check for the + * {@code RuntimePermission("dynalink.exportLinkersAutomatically")} when a + * security manager is present, to ensure that only trusted runtimes can + * automatically export their linkers into other runtimes. + * @see DynamicLinkerFactory#setClassLoader(ClassLoader) + */ +public abstract class GuardingDynamicLinkerExporter implements Supplier> { + /** + * The name of the runtime permission for creating instances of this class. + * Granting this permission to a language runtime allows it to export its + * linkers for automatic loading into other language runtimes. + */ + public static final String AUTOLOAD_PERMISSION_NAME = "dynalink.exportLinkersAutomatically"; + + private static final Permission AUTOLOAD_PERMISSION = new RuntimePermission(AUTOLOAD_PERMISSION_NAME); + + /** + * Creates a new linker exporter. If there is a security manager installed + * checks for the + * {@code RuntimePermission("dynalink.exportLinkersAutomatically")} runtime + * permission. This ensures only language runtimes granted this permission + * will be allowed to export their linkers for automatic loading. + * @throws SecurityException if the necessary runtime permission is not + * granted. + */ + protected GuardingDynamicLinkerExporter() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(AUTOLOAD_PERMISSION); + } + } +} diff -r 334cd3ebfa5e -r 6c945c826f36 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/package-info.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/package-info.java Tue Oct 20 23:34:16 2015 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/package-info.java Tue Oct 20 23:34:46 2015 +0200 @@ -389,17 +389,17 @@ *

Cross-language interoperability

* A {@code DynamicLinkerFactory} can be configured with a * {@link jdk.internal.dynalink.DynamicLinkerFactory#setClassLoader(ClassLoader) class - * loader}. It will try to instantiate all linker classes declared in - * {@code META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinker} resources - * visible to that class loader and compose them into the {@code DynamicLinker} - * it creates. This allows for interoperability between languages because if you - * have two language runtimes A and B deployed in your JVM and they expose their - * linkers through the above mechanism, language runtime A will have a - * language-specific linker instance from B and vice versa inside their - * {@code DynamicLinker} objects. This means that if an object from language - * runtime B gets passed to code from language runtime A, the linker from B will - * get a chance to link the call site in A when it encounters the object from B. - * @since 1.9 + * loader}. It will try to instantiate all + * {@link jdk.internal.dynalink.linker.GuardingDynamicLinkerExporter} classes visible to + * that class loader and compose the linkers they provide into the + * {@code DynamicLinker} it creates. This allows for interoperability between + * languages: if you have two language runtimes A and B deployed in your JVM and + * they export their linkers through the above mechanism, language runtime A + * will have a language-specific linker instance from B and vice versa inside + * their {@code DynamicLinker} objects. This means that if an object from + * language runtime B gets passed to code from language runtime A, the linker + * from B will get a chance to link the call site in A when it encounters the + * object from B. */ @jdk.Exported package jdk.internal.dynalink;