| /* Engine -- generic getInstance method. |
| Copyright (C) 2003, 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.java.security; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| |
| import java.security.NoSuchAlgorithmException; |
| import java.security.Provider; |
| import java.util.Enumeration; |
| |
| /** |
| * Generic implementation of the getInstance methods in the various |
| * engine classes in java.security. |
| * <p> |
| * These classes ({@link java.security.Signature} for example) can be |
| * thought of as the "chrome, upholstery, and steering wheel", and the SPI |
| * (service provider interface, e.g. {@link java.security.SignatureSpi}) |
| * classes can be thought of as the "engine" -- providing the actual |
| * functionality of whatever cryptographic algorithm the instance |
| * represents. |
| * |
| * @see Provider |
| * @author Casey Marshall |
| */ |
| public final class Engine |
| { |
| |
| // Constants. |
| // ------------------------------------------------------------------------ |
| |
| /** Prefix for aliases. */ |
| private static final String ALG_ALIAS = "Alg.Alias."; |
| |
| /** Maximum number of aliases to try. */ |
| private static final int MAX_ALIASES = 5; |
| |
| /** Argument list for no-argument constructors. */ |
| private static final Object[] NO_ARGS = new Object[0]; |
| |
| // Constructor. |
| // ------------------------------------------------------------------------ |
| |
| /** This class cannot be instantiated. */ |
| private Engine() { } |
| |
| // Class method. |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Get the implementation for <i>algorithm</i> for service |
| * <i>service</i> from <i>provider</i>. The service is e.g. |
| * "Signature", and the algorithm "DSA". |
| * |
| * @param service The service name. |
| * @param algorithm The name of the algorithm to get. |
| * @param provider The provider to get the implementation from. |
| * @return The engine class for the specified algorithm; the object |
| * returned is typically a subclass of the SPI class for that |
| * service, but callers should check that this is so. |
| * @throws NoSuchAlgorithmException If the implementation cannot be |
| * found or cannot be instantiated. |
| * @throws InvocationTargetException If the SPI class's constructor |
| * throws an exception. |
| * @throws IllegalArgumentException If any of the three arguments are null. |
| */ |
| public static Object getInstance(String service, String algorithm, |
| Provider provider) |
| throws InvocationTargetException, NoSuchAlgorithmException |
| { |
| return getInstance(service, algorithm, provider, NO_ARGS); |
| } |
| |
| /** |
| * Get the implementation for <i>algorithm</i> for service |
| * <i>service</i> from <i>provider</i>, passing <i>initArgs</i> to the |
| * SPI class's constructor (which cannot be null; pass a zero-length |
| * array if the SPI takes no arguments). The service is e.g. |
| * "Signature", and the algorithm "DSA". |
| * |
| * @param service The service name. |
| * @param algorithm The name of the algorithm to get. |
| * @param provider The provider to get the implementation from. |
| * @param initArgs The arguments to pass to the SPI class's |
| * constructor (cannot be null). |
| * @return The engine class for the specified algorithm; the object |
| * returned is typically a subclass of the SPI class for that |
| * service, but callers should check that this is so. |
| * @throws NoSuchAlgorithmException If the implementation cannot be |
| * found or cannot be instantiated. |
| * @throws InvocationTargetException If the SPI class's constructor |
| * throws an exception. |
| * @throws IllegalArgumentException If any of the four arguments are null. |
| */ |
| public static Object getInstance(String service, String algorithm, |
| Provider provider, Object[] initArgs) |
| throws InvocationTargetException, NoSuchAlgorithmException |
| { |
| if (service != null) |
| service = service.trim(); |
| |
| if (algorithm != null) |
| algorithm = algorithm.trim(); |
| |
| if (service == null || service.length() == 0 |
| || algorithm == null || algorithm.length() == 0 |
| || provider == null || initArgs == null) |
| throw new IllegalArgumentException(); |
| |
| |
| Enumeration enumer = provider.propertyNames(); |
| String key; |
| String alias; |
| int count = 0; |
| boolean algorithmFound = false; |
| |
| while (enumer.hasMoreElements()) |
| { |
| key = (String) enumer.nextElement(); |
| |
| if (key.equalsIgnoreCase(service + "." + algorithm)) |
| { |
| // remove the service portion from the key |
| algorithm = key.substring(service.length() + 1); |
| |
| algorithmFound = true; |
| break; |
| |
| } |
| else if (key.equalsIgnoreCase(ALG_ALIAS + service + "." + algorithm)) |
| { |
| |
| alias = (String) provider.getProperty(key); |
| |
| if (! algorithm.equalsIgnoreCase(alias)) // does not refer to itself |
| { |
| algorithm = alias; |
| if (count++ > MAX_ALIASES) |
| throw new NoSuchAlgorithmException("too many aliases"); |
| |
| // need to reset enumeration to now look for the alias |
| enumer = provider.propertyNames(); |
| } |
| } |
| } |
| |
| if (! algorithmFound) |
| { |
| throw new NoSuchAlgorithmException(algorithm); |
| } |
| |
| |
| // Find and instantiate the implementation. |
| Class clazz = null; |
| ClassLoader loader = provider.getClass().getClassLoader(); |
| Constructor constructor = null; |
| String error = algorithm; |
| |
| try |
| { |
| if (loader != null) |
| clazz = loader.loadClass(provider.getProperty(service+"."+algorithm)); |
| else |
| clazz = Class.forName(provider.getProperty(service+"."+algorithm)); |
| constructor = getCompatibleConstructor(clazz, initArgs); |
| return constructor.newInstance(initArgs); |
| } |
| catch (ClassNotFoundException cnfe) |
| { |
| error = "class not found: " + algorithm; |
| } |
| catch (IllegalAccessException iae) |
| { |
| error = "illegal access: " + iae.getMessage(); |
| } |
| catch (InstantiationException ie) |
| { |
| error = "instantiation exception: " + ie.getMessage(); |
| } |
| catch (ExceptionInInitializerError eiie) |
| { |
| error = "exception in initializer: " + eiie.getMessage(); |
| } |
| catch (SecurityException se) |
| { |
| error = "security exception: " + se.getMessage(); |
| } |
| catch (NoSuchMethodException nsme) |
| { |
| error = "no appropriate constructor found"; |
| } |
| |
| throw new NoSuchAlgorithmException(error); |
| } |
| |
| // Own methods. |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Find a constructor in the given class that can take the specified |
| * argument list, allowing any of which to be null. |
| * |
| * @param clazz The class from which to get the constructor. |
| * @param initArgs The argument list to be passed to the constructor. |
| * @return The constructor. |
| * @throws NoSuchMethodException If no constructor of the given class |
| * can take the specified argument array. |
| */ |
| private static Constructor getCompatibleConstructor(Class clazz, |
| Object[] initArgs) |
| throws NoSuchMethodException |
| { |
| Constructor[] c = clazz.getConstructors(); |
| outer:for (int i = 0; i < c.length; i++) |
| { |
| Class[] argTypes = c[i].getParameterTypes(); |
| if (argTypes.length != initArgs.length) |
| continue; |
| for (int j = 0; j < argTypes.length; j++) |
| { |
| if (initArgs[j] != null && |
| !argTypes[j].isAssignableFrom(initArgs[j].getClass())) |
| continue outer; |
| } |
| // If we reach this point, we know this constructor (c[i]) has |
| // the same number of parameters as the target parameter list, |
| // and all our parameters are either (1) null, or (2) assignable |
| // to the target parameter type. |
| return c[i]; |
| } |
| throw new NoSuchMethodException(); |
| } |
| } |