OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / security / Engine.java
1 /* Engine -- generic getInstance method.
2    Copyright (C) 2003, 2006  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.java.security;
39
40 import gnu.java.lang.CPStringBuilder;
41
42 import java.lang.reflect.Constructor;
43 import java.lang.reflect.InvocationTargetException;
44
45 import java.security.NoSuchAlgorithmException;
46 import java.security.Provider;
47 import java.util.Enumeration;
48
49 /**
50  * Generic implementation of the getInstance methods in the various
51  * engine classes in java.security.
52  * <p>
53  * These classes ({@link java.security.Signature} for example) can be
54  * thought of as the "chrome, upholstery, and steering wheel", and the SPI
55  * (service provider interface, e.g. {@link java.security.SignatureSpi})
56  * classes can be thought of as the "engine" -- providing the actual
57  * functionality of whatever cryptographic algorithm the instance
58  * represents.
59  *
60  * @see Provider
61  * @author Casey Marshall 
62  */
63 public final class Engine
64 {
65
66   // Constants.
67   // ------------------------------------------------------------------------
68
69   /** Prefix for aliases. */
70   private static final String ALG_ALIAS = "Alg.Alias.";
71
72   /** Maximum number of aliases to try. */
73   private static final int MAX_ALIASES = 5;
74
75   /** Argument list for no-argument constructors. */
76   private static final Object[] NO_ARGS = new Object[0];
77
78   // Constructor.
79   // ------------------------------------------------------------------------
80
81   /** This class cannot be instantiated. */
82   private Engine() { }
83
84   /**
85    * Return the implementation for <i>algorithm</i> for service <i>service</i>
86    * from <i>provider</i>. The service is e.g. "Signature", and the algorithm
87    * "DSA".
88    * 
89    * @param service The service name.
90    * @param algorithm The name of the algorithm to get.
91    * @param provider The provider to get the implementation from.
92    * @return The engine class for the specified algorithm; the object returned
93    *         is typically a subclass of the SPI class for that service, but
94    *         callers should check that this is so.
95    * @throws NoSuchAlgorithmException If the implementation cannot be found or
96    *           cannot be instantiated.
97    * @throws InvocationTargetException If the SPI class's constructor throws an
98    *           exception.
99    * @throws IllegalArgumentException If any of the three arguments is null.
100    */
101   public static Object getInstance(String service, String algorithm,
102                                    Provider provider)
103       throws InvocationTargetException, NoSuchAlgorithmException
104   {
105     return getInstance(service, algorithm, provider, NO_ARGS);
106   }
107
108   /**
109    * Return the implementation for <i>algorithm</i> for service <i>service</i>
110    * from <i>provider</i>, passing <i>initArgs</i> to the SPI class's
111    * constructor (which cannot be null; pass a zero-length array if the SPI
112    * takes no arguments). The service is e.g. "Signature", and the algorithm
113    * "DSA".
114    * 
115    * @param service The service name.
116    * @param algorithm The name of the algorithm to get.
117    * @param provider The provider to get the implementation from.
118    * @param initArgs The arguments to pass to the SPI class's constructor
119    *          (cannot be null).
120    * @return The engine class for the specified algorithm; the object returned
121    *         is typically a subclass of the SPI class for that service, but
122    *         callers should check that this is so.
123    * @throws NoSuchAlgorithmException If the implementation cannot be found or
124    *           cannot be instantiated.
125    * @throws InvocationTargetException If the SPI class's constructor throws an
126    *           exception.
127    * @throws IllegalArgumentException If any of the four arguments is
128    *           <code>null</code> or if either <code>service</code>, or
129    *           <code>algorithm</code> is an empty string.
130    */
131   public static Object getInstance(String service, String algorithm,
132                                    Provider provider, Object[] initArgs)
133       throws InvocationTargetException, NoSuchAlgorithmException
134   {
135     if (service == null)
136       throw new IllegalArgumentException("service MUST NOT be null");
137     service = service.trim();
138     if (service.length() == 0)
139       throw new IllegalArgumentException("service MUST NOT be empty");
140     if (algorithm == null)
141       throw new IllegalArgumentException("algorithm MUST NOT be null");
142     algorithm = algorithm.trim();
143     if (algorithm.length() == 0)
144       throw new IllegalArgumentException("algorithm MUST NOT be empty");
145     if (provider == null)
146       throw new IllegalArgumentException("provider MUST NOT be null");
147     if (initArgs == null)
148       throw new IllegalArgumentException("Constructor's parameters MUST NOT be null");
149
150     Enumeration enumer = provider.propertyNames();
151     String key = null;
152     String alias;
153     int count = 0;
154     boolean algorithmFound = false;
155     CPStringBuilder sb = new CPStringBuilder();
156     while (enumer.hasMoreElements())
157       {
158         key = (String) enumer.nextElement();
159         if (key.equalsIgnoreCase(service + "." + algorithm))
160           {
161             // remove the service portion from the key
162             algorithm = key.substring(service.length() + 1); 
163             algorithmFound = true;
164             break;
165           }
166         else if (key.equalsIgnoreCase(ALG_ALIAS + service + "." + algorithm))
167           {
168             alias = provider.getProperty(key);
169             if (! algorithm.equalsIgnoreCase(alias)) // does not refer to itself
170               {
171                 algorithm = alias;
172                 if (count++ > MAX_ALIASES)
173                   {
174                     sb.append("Algorithm [").append(algorithm)
175                         .append("] of type [").append(service)
176                         .append("] from provider [").append(provider)
177                         .append("] has too many aliases");
178                     throw new NoSuchAlgorithmException(sb.toString());
179                   }
180                 // need to reset enumeration to now look for the alias
181                 enumer = provider.propertyNames();
182               }
183           }
184       }
185
186     if (! algorithmFound)
187       {
188         sb.append("Algorithm [").append(algorithm).append("] of type [")
189             .append(service).append("] from provider [")
190             .append(provider).append("] is not found");
191         throw new NoSuchAlgorithmException(sb.toString());
192       }
193
194     // Find and instantiate the implementation
195     Class clazz = null;
196     ClassLoader loader = provider.getClass().getClassLoader();
197     Constructor constructor = null;
198     String className = provider.getProperty(key);
199     sb.append("Class [").append(className).append("] for algorithm [")
200         .append(algorithm).append("] of type [").append(service)
201         .append("] from provider [").append(provider).append("] ");
202     Throwable cause = null;
203     try
204       {
205         if (loader != null)
206           clazz = loader.loadClass(className);
207         else
208           clazz = Class.forName(className);
209         constructor = getCompatibleConstructor(clazz, initArgs);
210         return constructor.newInstance(initArgs);
211       }
212     catch (ClassNotFoundException x)
213       {
214         sb.append("cannot not be found");
215         cause = x;
216       }
217     catch (IllegalAccessException x)
218       {
219         sb.append("cannot be accessed");
220         cause = x;
221       }
222     catch (InstantiationException x)
223       {
224         sb.append("cannot be instantiated");
225         cause = x;
226       }
227     catch (ExceptionInInitializerError x)
228       {
229         sb.append("cannot be initialized");
230         cause = x;
231       }
232     catch (SecurityException x)
233       {
234         sb.append("caused a security violation");
235         cause = x;
236       }
237     catch (NoSuchMethodException x)
238       {
239         sb.append("does not have/expose an appropriate constructor");
240         cause = x;
241       }
242
243     NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString());
244     x.initCause(cause);
245     throw x;
246   }
247
248   /**
249    * Find a constructor in the given class that can take the specified
250    * argument list, allowing any of which to be null.
251    *
252    * @param clazz    The class from which to get the constructor.
253    * @param initArgs The argument list to be passed to the constructor.
254    * @return The constructor.
255    * @throws NoSuchMethodException If no constructor of the given class
256    *         can take the specified argument array.
257    */
258   private static Constructor getCompatibleConstructor(Class clazz,
259                                                       Object[] initArgs)
260     throws NoSuchMethodException
261   {
262     Constructor[] c = clazz.getConstructors();
263     outer:for (int i = 0; i < c.length; i++)
264       {
265         Class[] argTypes = c[i].getParameterTypes();
266         if (argTypes.length != initArgs.length)
267           continue;
268         for (int j = 0; j < argTypes.length; j++)
269           {
270             if (initArgs[j] != null &&
271                 !argTypes[j].isAssignableFrom(initArgs[j].getClass()))
272               continue outer;
273           }
274         // If we reach this point, we know this constructor (c[i]) has
275         // the same number of parameters as the target parameter list,
276         // and all our parameters are either (1) null, or (2) assignable
277         // to the target parameter type.
278         return c[i];
279       }
280     throw new NoSuchMethodException();
281   }
282 }