8055160: Support loading of Assistive Technology from service provider
authorptbrunet
Mon, 15 Jun 2015 12:36:35 -0500
changeset 31438 5593480edd2a
parent 31435 012c1c0bf5d6
child 31439 267a86a4cadf
8055160: Support loading of Assistive Technology from service provider Summary: Load assistive technolgy via service provider Reviewed-by: mchung, prr, alanb, ihse, serb Contributed-by: peter.brunet@oracle.com
jdk/make/copy/Copy-jdk.accessibility.gmk
jdk/src/java.desktop/share/classes/java/awt/Toolkit.java
jdk/src/java.desktop/share/classes/javax/accessibility/AccessibilityProvider.java
jdk/src/jdk.accessibility/windows/classes/META-INF/services/javax.accessibility.AccessibilityProvider
jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/ProviderImpl.java
jdk/src/jdk.accessibility/windows/conf/accessibility.properties
jdk/test/javax/accessibility/AccessibilityProvider/BarProvider.java
jdk/test/javax/accessibility/AccessibilityProvider/FooProvider.java
jdk/test/javax/accessibility/AccessibilityProvider/Load.java
jdk/test/javax/accessibility/AccessibilityProvider/UnusedProvider.java
jdk/test/javax/accessibility/AccessibilityProvider/accessibilityProvider.sp
jdk/test/javax/accessibility/AccessibilityProvider/basic.sh
--- a/jdk/make/copy/Copy-jdk.accessibility.gmk	Mon Jun 15 14:43:31 2015 +0300
+++ b/jdk/make/copy/Copy-jdk.accessibility.gmk	Mon Jun 15 12:36:35 2015 -0500
@@ -31,17 +31,12 @@
   TARGETS += $(INCLUDE_DST_OS_DIR)/bridge/AccessBridgeCallbacks.h \
       $(INCLUDE_DST_OS_DIR)/bridge/AccessBridgeCalls.h \
       $(INCLUDE_DST_OS_DIR)/bridge/AccessBridgePackages.h \
-      $(INCLUDE_DST_OS_DIR)/bridge/AccessBridgeCalls.c \
-      $(CONF_DST_DIR)/accessibility.properties
+      $(INCLUDE_DST_OS_DIR)/bridge/AccessBridgeCalls.c
 
   $(INCLUDE_DST_OS_DIR)/bridge/%: \
       $(JDK_TOPDIR)/src/jdk.accessibility/windows/native/include/bridge/%
 		$(install-file)
 
-  $(CONF_DST_DIR)/accessibility.properties: \
-      $(JDK_TOPDIR)/src/jdk.accessibility/windows/conf/accessibility.properties
-		$(install-file)
-
 endif
 
 ################################################################################
--- a/jdk/src/java.desktop/share/classes/java/awt/Toolkit.java	Mon Jun 15 14:43:31 2015 +0300
+++ b/jdk/src/java.desktop/share/classes/java/awt/Toolkit.java	Mon Jun 15 12:36:35 2015 -0500
@@ -58,6 +58,14 @@
 import sun.awt.SunToolkit;
 import sun.util.CoreResourceBundleControl;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.accessibility.AccessibilityProvider;
+
 /**
  * This class is the abstract superclass of all actual
  * implementations of the Abstract Window Toolkit. Subclasses of
@@ -420,7 +428,7 @@
                     }
                 }
 
-                // Get the names of any assistive technolgies to load.  First
+                // Get the names of any assistive technologies to load.  First
                 // check the system property and then check the properties
                 // file.
                 String classNames = System.getProperty("javax.accessibility.assistive_technologies");
@@ -436,85 +444,125 @@
     }
 
     /**
-     * Loads additional classes into the VM, using the property
-     * 'assistive_technologies' specified in the Sun reference
-     * implementation by a line in the 'accessibility.properties'
-     * file.  The form is "assistive_technologies=..." where
-     * the "..." is a comma-separated list of assistive technology
-     * classes to load.  Each class is loaded in the order given
-     * and a single instance of each is created using
-     * Class.forName(class).newInstance().  All errors are handled
-     * via an AWTError exception.
+     * Rethrow the AWTError but include the cause.
+     *
+     * @param s the error message
+     * @param e the original exception
+     * @throw the new AWTError including the cause (the original exception)
+     */
+    private static void newAWTError(Throwable e, String s) {
+        AWTError newAWTError = new AWTError(s);
+        newAWTError.initCause(e);
+        throw newAWTError;
+    }
+
+    /**
+     * When a service provider for Assistive Technology is not found look for a
+     * supporting class on the class path and instantiate it.
      *
-     * <p>The assumption is made that assistive technology classes are supplied
-     * as part of INSTALLED (as opposed to: BUNDLED) extensions or specified
-     * on the class path
-     * (and therefore can be loaded using the class loader returned by
-     * a call to <code>ClassLoader.getSystemClassLoader</code>, whose
-     * delegation parent is the extension class loader for installed
-     * extensions).
+     * @param atName the name of the class to be loaded
+     */
+    private static void fallbackToLoadClassForAT(String atName) {
+        try {
+            Class.forName(atName, false, ClassLoader.getSystemClassLoader()).newInstance();
+        } catch (ClassNotFoundException e) {
+            newAWTError(e, "Assistive Technology not found: " + atName);
+        } catch (InstantiationException e) {
+            newAWTError(e, "Could not instantiate Assistive Technology: " + atName);
+        } catch (IllegalAccessException e) {
+            newAWTError(e, "Could not access Assistive Technology: " + atName);
+        } catch (Exception e) {
+            newAWTError(e, "Error trying to install Assistive Technology: " + atName);
+        }
+    }
+
+    /**
+     * Loads accessibility support using the property assistive_technologies.
+     * The form is assistive_technologies= followed by a comma-separated list of
+     * assistive technology providers to load.  The order in which providers are
+     * loaded is determined by the order in which the ServiceLoader discovers
+     * implementations of the AccessibilityProvider interface, not by the order
+     * of provider names in the property list.  When a provider is found its
+     * accessibility implementation will be started by calling the provider's
+     * activate method.  All errors are handled via an AWTError exception.
      */
     private static void loadAssistiveTechnologies() {
         // Load any assistive technologies
         if (atNames != null) {
             ClassLoader cl = ClassLoader.getSystemClassLoader();
-            StringTokenizer parser = new StringTokenizer(atNames," ,");
-            String atName;
-            while (parser.hasMoreTokens()) {
-                atName = parser.nextToken();
+            Set<String> names = Arrays.stream(atNames.split(","))
+                                      .map(String::trim)
+                                      .collect(Collectors.toSet());
+            final Map<String, AccessibilityProvider> providers = new HashMap<>();
+            AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
                 try {
-                    Class<?> clazz;
-                    if (cl != null) {
-                        clazz = cl.loadClass(atName);
-                    } else {
-                        clazz = Class.forName(atName);
+                    for (AccessibilityProvider p : ServiceLoader.load(AccessibilityProvider.class, cl)) {
+                        String name = p.getName();
+                        if (names.contains(name) && !providers.containsKey(name)) {
+                            p.activate();
+                            providers.put(name, p);
+                        }
                     }
-                    clazz.newInstance();
-                } catch (ClassNotFoundException e) {
-                    throw new AWTError("Assistive Technology not found: "
-                            + atName);
-                } catch (InstantiationException e) {
-                    throw new AWTError("Could not instantiate Assistive"
-                            + " Technology: " + atName);
-                } catch (IllegalAccessException e) {
-                    throw new AWTError("Could not access Assistive"
-                            + " Technology: " + atName);
-                } catch (Exception e) {
-                    throw new AWTError("Error trying to install Assistive"
-                            + " Technology: " + atName + " " + e);
+                } catch (java.util.ServiceConfigurationError | Exception e) {
+                    newAWTError(e, "Could not load or activate service provider");
                 }
-            }
+                return null;
+            });
+            names.stream()
+                 .filter(n -> !providers.containsKey(n))
+                 .forEach(Toolkit::fallbackToLoadClassForAT);
         }
     }
 
     /**
      * Gets the default toolkit.
      * <p>
-     * If a system property named <code>"java.awt.headless"</code> is set
-     * to <code>true</code> then the headless implementation
-     * of <code>Toolkit</code> is used.
+     * If a system property named {@code "java.awt.headless"} is set
+     * to {@code true} then the headless implementation
+     * of {@code Toolkit} is used.
      * <p>
-     * If there is no <code>"java.awt.headless"</code> or it is set to
-     * <code>false</code> and there is a system property named
-     * <code>"awt.toolkit"</code>,
+     * If there is no {@code "java.awt.headless"} or it is set to
+     * {@code false} and there is a system property named
+     * {@code "awt.toolkit"},
      * that property is treated as the name of a class that is a subclass
-     * of <code>Toolkit</code>;
+     * of {@code Toolkit};
      * otherwise the default platform-specific implementation of
-     * <code>Toolkit</code> is used.
+     * {@code Toolkit} is used.
+     * <p>
+     * If this Toolkit is not a headless implementation and if they exist, service
+     * providers of {@link javax.accessibility.AccessibilityProvider} will be loaded
+     * if specified by the system property
+     * {@code javax.accessibility.assistive_technologies}.
      * <p>
-     * Also loads additional classes into the VM, using the property
-     * 'assistive_technologies' specified in the Sun reference
-     * implementation by a line in the 'accessibility.properties'
-     * file.  The form is "assistive_technologies=..." where
-     * the "..." is a comma-separated list of assistive technology
-     * classes to load.  Each class is loaded in the order given
-     * and a single instance of each is created using
-     * Class.forName(class).newInstance().  This is done just after
-     * the AWT toolkit is created.  All errors are handled via an
-     * AWTError exception.
-     * @return    the default toolkit.
+     * An example of setting this property is to invoke Java with
+     * {@code -Djavax.accessibility.assistive_technologies=MyServiceProvider}.
+     * In addition to MyServiceProvider other service providers can be specified
+     * using a comma separated list.  Service providers are loaded after the AWT
+     * toolkit is created. All errors are handled via an AWTError exception.
+     * <p>
+     * The names specified in the assistive_technologies property are used to query
+     * each service provider implementation.  If the requested name matches the
+     * {@linkplain AccessibilityProvider#getName name} of the service provider, the
+     * {@link AccessibilityProvider#activate} method will be invoked to activate the
+     * matching service provider.
+     *
+     * @implSpec
+     * If assistive technology service providers are not specified with a system
+     * property this implementation will look in a properties file located as follows:
+     * <ul>
+     * <li> {@code ${user.home}/.accessibility.properties}
+     * <li> {@code ${java.home}/conf/accessibility.properties}
+     * </ul>
+     * Only the first of these files to be located will be consulted.  The requested
+     * service providers are specified by setting the {@code assistive_technologies=}
+     * property.  A single provider or a comma separated list of providers can be
+     * specified.
+     *
+     * @return     the default toolkit.
      * @exception  AWTError  if a toolkit could not be found, or
      *                 if one could not be accessed or instantiated.
+     * @see java.util.ServiceLoader
+     * @see javax.accessibility.AccessibilityProvider
      */
     public static synchronized Toolkit getDefaultToolkit() {
         if (toolkit == null) {
@@ -550,7 +598,9 @@
                     return null;
                 }
             });
-            loadAssistiveTechnologies();
+            if (!GraphicsEnvironment.isHeadless()) {
+                loadAssistiveTechnologies();
+            }
         }
         return toolkit;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/accessibility/AccessibilityProvider.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,93 @@
+/*
+ * 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 javax.accessibility;
+
+/**
+ * Service Provider Interface (SPI) for Assistive Technology.
+ * <p>
+ * This service provider class provides mappings from the platform
+ * specific accessibility APIs to the Java Accessibility API.
+ * <p>
+ * Each service provider implementation is named and can be activated via the
+ * {@link #activate} method. Service providers can be loaded when the default
+ * {@link java.awt.Toolkit toolkit} is initialized.
+ *
+ * @apiNote There will typically be one provider per platform, such as Windows
+ * or Linux, to support accessibility for screen readers and magnifiers.  However,
+ * more than one service provider can be activated.  For example, a test tool
+ * which provides visual results obtained by interrogating the Java Accessibility
+ * API can be activated along with the activation of the support for screen readers
+ * and screen magnifiers.
+ *
+ * @see java.awt.Toolkit#getDefaultToolkit
+ * @see java.util.ServiceLoader
+ * @since 1.9
+ */
+public abstract class AccessibilityProvider {
+
+    /**
+     * Initializes a new accessibility provider.
+     *
+     * @throws  SecurityException
+     *          If a security manager has been installed and it denies
+     *          {@link RuntimePermission}{@code("accessibilityProvider")}
+     */
+    protected AccessibilityProvider() {
+        // Use a permission check when calling a private constructor to check that
+        // the proper security permission has been granted before the Object superclass
+        // is called.  If an exception is thrown before the Object superclass is
+        // constructed a finalizer in a subclass of this class will not be run.
+        // This protects against a finalizer vulnerability.
+        this(checkPermission());
+    }
+
+    private AccessibilityProvider(Void ignore) { }
+
+    /**
+     * If this code is running with a security manager and if the permission
+     * "accessibilityProvider" has not been granted SecurityException will be thrown.
+     *
+     */
+    private static Void checkPermission() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+            sm.checkPermission(new RuntimePermission("accessibilityProvider"));
+        return null;
+    }
+
+    /**
+     * Returns the name of this service provider.  This name is used to locate a
+     * requested service provider.
+     *
+     * @return the name of this service provider
+     */
+    public abstract String getName();
+
+    /**
+     * Activates the support provided by this service provider.
+     */
+    public abstract void activate();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.accessibility/windows/classes/META-INF/services/javax.accessibility.AccessibilityProvider	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,26 @@
+# 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.
+
+
+com.sun.java.accessibility.ProviderImpl
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/ProviderImpl.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,61 @@
+/*
+ * 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 com.sun.java.accessibility;
+
+import javax.accessibility.AccessibilityProvider;
+
+/* This class provided methods to identify and activate the mapping from the
+ * JavaAccessBridge API to the Java Accessibility API.
+ */
+public final class ProviderImpl extends AccessibilityProvider {
+    /**
+     * Typically the service name returned by the name() method would be a simple
+     * name such as JavaAccessBridge, but the following name is used for compatibility
+     * with prior versions of ${user.home}/.accessibility.properties and
+     * ${java.home}/conf/accessibility.properties where the text on the
+     * assistive.technologies= line is a fully qualified class name. As of Java 9
+     * class names are no longer used to identify assistive technology implementations.
+     * If the properties file exists the installer will not replace it thus the
+     * need for compatibility.
+     */
+    private final String name = "com.sun.java.accessibility.AccessBridge";
+
+    public ProviderImpl() {}
+
+    public String getName() {
+        return name;
+    }
+
+    public void activate() {
+        /**
+         * Note that the AccessBridge is instantiated here rather than in the
+         * constructor.  If the caller determines that this object is named
+         * "com.sun.java.accessibility.AccessBridge" then the caller will call
+         * start to instantiate the AccessBridge which will in turn activate it.
+         */
+        new AccessBridge();
+    }
+
+}
--- a/jdk/src/jdk.accessibility/windows/conf/accessibility.properties	Mon Jun 15 14:43:31 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-#
-# Load the Java Access Bridge class into the JVM
-#
-#assistive_technologies=com.sun.java.accessibility.AccessBridge
-#screen_magnifier_present=true
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/BarProvider.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import javax.accessibility.AccessibilityProvider;
+
+public final class BarProvider extends AccessibilityProvider {
+    private final String name = "BarProvider";
+
+    public String getName() {
+        return name;
+    }
+
+    public void activate() {
+        // Write to log to indicate activate was called.
+        try (PrintWriter writer = new PrintWriter("BarProvider.txt")) {
+            writer.println(" BarProvider-activated");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/FooProvider.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import javax.accessibility.AccessibilityProvider;
+import java.io.UncheckedIOException;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public final class FooProvider extends AccessibilityProvider {
+
+    private final String name = "FooProvider";
+
+    public String getName() {
+        return name;
+    }
+
+    public void activate() {
+        // Write to log to indicate activate was called.
+        try (PrintWriter writer = new PrintWriter("FooProvider.txt")) {
+            writer.println("FooProvider-activated");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/Load.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.awt.AWTError;
+import java.awt.Toolkit;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import javax.accessibility.AccessibilityProvider;
+
+public class Load {
+
+    public static void main(String[] args) {
+        // args[0]: "pass" or "fail" (the expected result)
+        // args[1]: "<first provider name>"
+        // args[2]: "<optional second provider name>"
+
+        boolean passExpected = args[0].equals("pass");
+
+        // Fill Set with provider names that were requested.
+        // The providers may or may not be available:
+        // - available: FooProvider, BarProvider
+        // - not available: NoProvider
+        List<String> requestedNames = new ArrayList<>();
+        for (int i = 1; i < args.length; ++i) {
+            requestedNames.add(args[i]);
+        }
+        // cleanup files from any prior run
+        for (String name : requestedNames) {
+            File f = new File(name + ".txt");
+            f.delete();
+        }
+        // Activate getDefaultToolkit which will in turn activate the providers
+        try {
+            Toolkit.getDefaultToolkit();
+        } catch (AWTError e) {
+            if (passExpected) {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        // Toolkit.getDefaultToolkit() already went through all the service
+        // providers, loading and activating the requested ones, but now we need
+        // to see if they actually got activated.
+        // Go through the providers that were requested, for each one:
+        //   If it was activated pass
+        //   else fail (throw exception)
+        boolean failure = false;
+        String failingName = "";
+        for (String name : requestedNames) {
+            File f = new File(name + ".txt");
+            if (!f.exists()) {
+                failure = true;
+                failingName = name;
+                break;
+            }
+        } // if get to here, no issues, so try next provider
+        if (failure && passExpected) {
+            throw new RuntimeException(failingName + " was not activated");
+        }
+        if (!failure && !passExpected) {
+            String s = "Test passed but a failure was expected.  ";
+            s += "The requested providers were:\n";
+            for (String name : requestedNames) {
+                s += ("  " + name + "\n");
+            }
+            throw new RuntimeException(s);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/UnusedProvider.java	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import javax.accessibility.AccessibilityProvider;
+
+public final class UnusedProvider extends AccessibilityProvider {
+
+    private static final String name = "UnusedProvider";
+
+    public String getName() {
+        return name;
+    }
+
+    public void activate() {
+        // Write to log to indicate activate was called.
+        try (PrintWriter writer = new PrintWriter("UnusedProvider.txt")) {
+            writer.println("UnusedProvider-activated");
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/accessibilityProvider.sp	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,4 @@
+grant { 
+    permission java.lang.RuntimePermission "accessibilityProvider";
+    permission java.io.FilePermission "*", "read,write,delete";
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/accessibility/AccessibilityProvider/basic.sh	Mon Jun 15 12:36:35 2015 -0500
@@ -0,0 +1,114 @@
+#
+# 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.
+#
+# 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.
+#
+# @test
+# @bug 8055160
+# @summary Unit test for javax.accessibility.AccessibilitySPI
+#
+# @build Load FooProvider BarProvider UnusedProvider
+# @run shell basic.sh
+
+# Command-line usage: sh basic.sh /path/to/build
+
+if [ -z "$TESTJAVA" ]; then
+  if [ $# -lt 1 ]; then exit 1; fi
+  TESTJAVA="$1"
+  TESTSRC=`pwd`
+  TESTCLASSES="`pwd`"
+fi
+
+JAVA="$TESTJAVA/bin/java"
+
+OS=`uname -s`
+case "$OS" in
+    SunOS | Darwin | AIX )
+      FS='/'
+      SEP=':' ;;
+    Linux )
+      FS='/'
+      SEP=':' ;;
+    * )
+      FS='\\'
+      SEP='\;' ;;
+esac
+
+TESTD=x.test
+rm -rf $TESTD
+mkdir -p $TESTD
+
+mv $TESTCLASSES/FooProvider.class $TESTD
+mv $TESTCLASSES/BarProvider.class $TESTD
+mv $TESTCLASSES/UnusedProvider.class $TESTD
+mkdir -p $TESTD/META-INF/services
+echo FooProvider >$TESTD/META-INF/services/javax.accessibility.AccessibilityProvider
+echo BarProvider >>$TESTD/META-INF/services/javax.accessibility.AccessibilityProvider
+echo UnusedProvider >>$TESTD/META-INF/services/javax.accessibility.AccessibilityProvider
+
+
+failures=0
+
+go() {
+  CP="$TESTCLASSES$SEP$TESTD"
+  echo ''
+  sh -xc "$JAVA $SECURITY_MANAGER -Djavax.accessibility.assistive_technologies=$PROVIDER1$COMMA$PROVIDER2 -cp $CP Load $1 $2 $3" 2>&1
+  if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
+}
+
+# find one provider
+PROVIDER1="FooProvider"
+go pass $PROVIDER1
+
+# start using security manager
+SECURITY_MANAGER="-Djava.security.manager -Djava.security.policy=$TESTSRC/accessibilityProvider.sp"
+
+# find one provider (with security manager)
+go pass $PROVIDER1
+SECURITY_MANAGER=
+
+# fail if no provider found
+PROVIDER1="NoProvider"
+go fail $PROVIDER1
+
+# setup for two providers
+COMMA=","
+
+# find two providers, both exist
+PROVIDER1="FooProvider"
+PROVIDER2="BarProvider"
+go pass $PROVIDER1 $PROVIDER2
+
+# find two providers, where second one doesn't exist
+PROVIDER1="FooProvider"
+PROVIDER2="NoProvider"
+go fail $PROVIDER1 $PROVIDER2
+
+# find two providers, where first one doesn't exist
+PROVIDER1="NoProvider"
+PROVIDER2="BarProvider"
+go fail $PROVIDER1 $PROVIDER2
+
+echo ''
+if [ $failures -gt 0 ];
+  then echo "$failures case(s) failed";
+  else echo "All cases passed"; fi
+exit $failures
+