--- 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<ServiceConfigurationError> 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<? extends GuardingDynamicLinker> 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<? extends GuardingDynamicLinker> 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<GuardingDynamicLinker> discovered = new LinkedList<>();
- final ServiceLoader<GuardingDynamicLinker> linkerLoader = ServiceLoader.load(GuardingDynamicLinker.class, effectiveClassLoader);
- for(final GuardingDynamicLinker linker: linkerLoader) {
- discovered.add(linker);
- }
+ final List<GuardingDynamicLinker> discovered = discoverAutoLoadLinkers();
// Now, concatenate ...
final List<GuardingDynamicLinker> 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:
+ * <ul>
+ * <li>did not have the runtime permission named
+ * {@link GuardingDynamicLinkerExporter#AUTOLOAD_PERMISSION_NAME} in a
+ * system with a security manager, or</li>
+ * <li>returned null from {@link GuardingDynamicLinkerExporter#get()}, or</li>
+ * <li>the list returned from {@link GuardingDynamicLinkerExporter#get()}
+ * had a null element.</li>
+ * </ul>
+ * @return an immutable list of encountered
+ * {@link ServiceConfigurationError}s. Can be empty.
+ */
+ public List<ServiceConfigurationError> getAutoLoadingErrors() {
+ return Collections.unmodifiableList(autoLoadingErrors);
+ }
+
+ private List<GuardingDynamicLinker> discoverAutoLoadLinkers() {
+ autoLoadingErrors = new LinkedList<>();
+ final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader();
+ final List<GuardingDynamicLinker> discovered = new LinkedList<>();
+ try {
+ final ServiceLoader<GuardingDynamicLinkerExporter> linkerLoader =
+ AccessController.doPrivileged((PrivilegedAction<ServiceLoader<GuardingDynamicLinkerExporter>>)()-> {
+ if (effectiveClassLoader == null) {
+ return ServiceLoader.loadInstalled(GuardingDynamicLinkerExporter.class);
+ }
+ return ServiceLoader.load(GuardingDynamicLinkerExporter.class, effectiveClassLoader);
+ });
+
+ for(final Iterator<GuardingDynamicLinkerExporter> 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<ClassLoader>() {
@Override
@@ -436,14 +505,18 @@
}
}
- private static <T> List<T> reqireNonNullElements(final List<T> list) {
+ private static <T> List<T> copyListRequireNonNullElements(final List<T> list) {
if (list == null) {
return null;
}
+ return new ArrayList<>(requireNonNullElements(list, ()->"List has at least one null element"));
+ }
+
+ private static <T> List<T> requireNonNullElements(final List<T> list, final Supplier<String> msgSupplier) {
for(final T t: list) {
- Objects.requireNonNull(t);
+ Objects.requireNonNull(t, msgSupplier);
}
- return new ArrayList<>(list);
+ return list;
}
}
--- 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.
* <p>
- * Languages can declare the linkers they want to expose to other runtimes for
- * {@link DynamicLinkerFactory#setClassLoader(ClassLoader) automatic discovery}
- * in <tt>META-INF/services/jdk.internal.dynalink.linker.GuardingDynamicLinker</tt>
- * resources of their JAR files.
- * <p>
* 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.
+ * <p>
+ * Languages can export linkers to other language runtimes for
+ * {@link DynamicLinkerFactory#setClassLoader(ClassLoader) automatic discovery}
+ * using a {@link GuardingDynamicLinkerExporter}.
*/
public interface GuardingDynamicLinker {
/**
--- /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<List<GuardingDynamicLinker>> {
+ /**
+ * 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);
+ }
+ }
+}
--- 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 @@
* <h2>Cross-language interoperability</h2>
* 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;