nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java
changeset 33340 6c945c826f36
parent 33339 334cd3ebfa5e
child 33341 cc9fa3638714
--- 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;
     }
 
 }