src/java.base/share/classes/sun/security/jca/ProviderConfig.java

Print this page
7191662: JCE providers should be located via ServiceLoader

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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

@@ -25,18 +25,19 @@
 
 package sun.security.jca;
 
 import java.io.File;
 import java.lang.reflect.*;
+import java.util.*;
 
 import java.security.*;
 
 import sun.security.util.PropertyExpander;
 
 /**
- * Class representing a configured provider. Encapsulates configuration
- * (className plus optional argument), the provider loading logic, and
+ * Class representing a configured provider which encapsulates configuration
+ * (className + optional argument), the provider loading logic, and
  * the loaded Provider object itself.
  *
  * @author  Andreas Sterbenz
  * @since   1.5
  */

@@ -54,19 +55,14 @@
         "${java.home}/conf/security/sunpkcs11-solaris.cfg";
 
     // maximum number of times to try loading a provider before giving up
     private final static int MAX_LOAD_TRIES = 30;
 
-    // parameters for the Provider(String) constructor,
-    // use by doLoadProvider()
-    private final static Class<?>[] CL_STRING = { String.class };
-
     // name of the provider class
     private final String className;
 
-    // argument to the provider constructor,
-    // empty string indicates no-arg constructor
+    // argument to the Provider.configure() call, never null
     private final String argument;
 
     // number of times we have already tried to load this provider
     private int tries;
 

@@ -170,10 +166,21 @@
             return p;
         }
         if (shouldLoad() == false) {
             return null;
         }
+
+        // Create providers which are in java.base directly
+        if (className.equals("sun.security.provider.Sun")) {
+            p = new sun.security.provider.Sun();
+        } else if (className.equals("sun.security.rsa.SunRsaSign")) {
+            p = new sun.security.rsa.SunRsaSign();
+        } else if (className.equals("com.sun.crypto.provider.SunJCE")) {
+            p = new com.sun.crypto.provider.SunJCE();
+        } else if (className.equals("com.sun.net.ssl.internal.ssl.Provider")) {
+            p = new com.sun.net.ssl.internal.ssl.Provider();
+        } else {
         if (isLoading) {
             // because this method is synchronized, this can only
             // happen if there is recursion.
             if (debug != null) {
                 debug.println("Recursion loading provider: " + this);

@@ -186,10 +193,11 @@
             tries++;
             p = doLoadProvider();
         } finally {
             isLoading = false;
         }
+        }
         provider = p;
         return p;
     }
 
     /**

@@ -206,65 +214,39 @@
         return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
             public Provider run() {
                 if (debug != null) {
                     debug.println("Loading provider: " + ProviderConfig.this);
                 }
+                ProviderLoader pl = new ProviderLoader();
                 try {
-                    ClassLoader cl = ClassLoader.getSystemClassLoader();
-                    Class<?> provClass;
-                    if (cl != null) {
-                        provClass = cl.loadClass(className);
-                    } else {
-                        provClass = Class.forName(className);
+                    Provider p = pl.load(className);
+                    if (p != null) {
+                        if (hasArgument()) {
+                            p = p.configure(argument);
                     }
-                    Object obj;
-                    if (hasArgument() == false) {
-                        obj = provClass.newInstance();
-                    } else {
-                        Constructor<?> cons = provClass.getConstructor(CL_STRING);
-                        obj = cons.newInstance(argument);
-                    }
-                    if (obj instanceof Provider) {
                         if (debug != null) {
-                            debug.println("Loaded provider " + obj);
+                            debug.println("Loaded provider " + p.getName());
                         }
-                        return (Provider)obj;
                     } else {
                         if (debug != null) {
-                            debug.println(className + " is not a provider");
+                            debug.println("Error loading " + className);
                         }
                         disableLoad();
-                        return null;
                     }
+                    return p;
                 } catch (Exception e) {
-                    Throwable t;
-                    if (e instanceof InvocationTargetException) {
-                        t = ((InvocationTargetException)e).getCause();
+                    if (e instanceof ProviderException) {
+                        // pass up
+                        throw e;
                     } else {
-                        t = e;
-                    }
                     if (debug != null) {
-                        debug.println("Error loading provider " + ProviderConfig.this);
-                        t.printStackTrace();
+                            debug.println("Error loading " + className);
+                            e.printStackTrace();
                     }
-                    // provider indicates fatal error, pass through exception
-                    if (t instanceof ProviderException) {
-                        throw (ProviderException)t;
-                    }
-                    // provider indicates that loading should not be retried
-                    if (t instanceof UnsupportedOperationException) {
                         disableLoad();
-                    }
                     return null;
-                } catch (ExceptionInInitializerError err) {
-                    // no sufficient permission to initialize provider class
-                    if (debug != null) {
-                        debug.println("Error loading provider " + ProviderConfig.this);
-                        err.printStackTrace();
                     }
-                    disableLoad();
-                    return null;
                 }
             }
         });
     }
 

@@ -287,6 +269,120 @@
                 }
             }
         });
     }
 
+    // Inner class for loading security providers listed in java.security file
+    private static final class ProviderLoader {
+        ServiceLoader<Provider> services;
+
+        ProviderLoader() {
+            // VM should already been booted at this point, if not
+            // - Only providers in java.base should be loaded, don't use
+            //   ServiceLoader
+            // - ClassLoader.getSystemClassLoader() will throw InternalError
+            services = ServiceLoader.load(java.security.Provider.class,
+                                          ClassLoader.getSystemClassLoader());
+        }
+
+        /**
+         * Loads the provider with the specified class name.
+         *
+         * @param name the name of the provider class
+         * @return the Provider, or null if it cannot be found or loaded
+         * @throws ProviderException all other exceptions are ignored
+         */
+        public Provider load(String classname) {
+            if (debug != null) {
+                debug.println("Attempt to load " + classname + " using SL");
+            }
+            Iterator<Provider> iter = services.iterator();
+            while (iter.hasNext()) {
+                try {
+                    Provider p = iter.next();
+                    if (debug != null) {
+                        debug.println("Found SL Provider named " + p.getName());
+                    }
+                    if (p.getClass().getName().equals(classname)) {
+                        return p;
+                    }
+                } catch (SecurityException | ServiceConfigurationError |
+                         InvalidParameterException ex) {
+                    // if provider loading fail due to security permission,
+                    // log it and move on to next provider
+                    if (debug != null) {
+                        debug.println("Encountered " + ex +
+                            " while iterating through SL, ignore and move on");
+                            ex.printStackTrace();
+                    }
+                }
+            }
+            // No success with ServiceLoader. Try loading provider the legacy,
+            // i.e. pre-module, way via reflection
+            try {
+                return legacyLoad(classname);
+            } catch (ProviderException pe) {
+                // pass through
+                throw pe;
+            } catch (Exception ex) {
+                // logged and ignored
+                if (debug != null) {
+                    debug.println("Encountered " + ex +
+                        " during legacy load of " + classname);
+                        ex.printStackTrace();
+                }
+                return null;
+            }
+        }
+
+        private Provider legacyLoad(String classname) {
+
+            if (debug != null) {
+                debug.println("Loading legacy provider: " + classname);
+            }
+
+            try {
+                Class<?> provClass =
+                    ClassLoader.getSystemClassLoader().loadClass(classname);
+
+                // only continue if the specified class extends Provider
+                if (!Provider.class.isAssignableFrom(provClass)) {
+                    if (debug != null) {
+                        debug.println(classname + " is not a provider");
+                    }
+                    return null;
+                }
+
+                Provider p = AccessController.doPrivileged
+                    (new PrivilegedExceptionAction<Provider>() {
+                    public Provider run() throws Exception {
+                        return (Provider) provClass.newInstance();
+                    }
+                });
+                return p;
+            } catch (Exception e) {
+                Throwable t;
+                if (e instanceof InvocationTargetException) {
+                    t = ((InvocationTargetException)e).getCause();
+                } else {
+                    t = e;
+                }
+                if (debug != null) {
+                    debug.println("Error loading legacy provider " + classname);
+                    t.printStackTrace();
+                }
+                // provider indicates fatal error, pass through exception
+                if (t instanceof ProviderException) {
+                    throw (ProviderException) t;
+                }
+                return null;
+            } catch (ExceptionInInitializerError | NoClassDefFoundError err) {
+                // no sufficient permission to access/initialize provider class
+                if (debug != null) {
+                    debug.println("Error loading legacy provider " + classname);
+                    err.printStackTrace();
+                }
+                return null;
+            }
+        }
+    }
 }