KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > security > Provider


1 /*
2  * @(#)Provider.java 1.64 05/04/08
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.security;
9
10 import java.io.*;
11 import java.util.*;
12 import static java.util.Locale.ENGLISH JavaDoc;
13 import java.lang.ref.*;
14 import java.lang.reflect.*;
15
16 import java.security.cert.CertStoreParameters JavaDoc;
17
18 /**
19  * This class represents a "provider" for the
20  * Java Security API, where a provider implements some or all parts of
21  * Java Security. Services that a provider may implement include:
22  *
23  * <ul>
24  *
25  * <li>Algorithms (such as DSA, RSA, MD5 or SHA-1).
26  *
27  * <li>Key generation, conversion, and management facilities (such as for
28  * algorithm-specific keys).
29  *
30  *</ul>
31  *
32  * <p>Each provider has a name and a version number, and is configured
33  * in each runtime it is installed in.
34  *
35  * <p>See <a href =
36  * "../../../guide/security/CryptoSpec.html#Provider">The Provider Class</a>
37  * in the "Java Cryptography Architecture API Specification &amp; Reference"
38  * for information about how a particular type of provider, the
39  * cryptographic service provider, works and is installed. However,
40  * please note that a provider can be used to implement any security
41  * service in Java that uses a pluggable architecture with a choice
42  * of implementations that fit underneath.
43  *
44  * <p>Some provider implementations may encounter unrecoverable internal
45  * errors during their operation, for example a failure to communicate with a
46  * security token. A {@link ProviderException} should be used to indicate
47  * such errors.
48  *
49  * <p>The service type <code>Provider</code> is reserved for use by the
50  * security framework. Services of this type cannot be added, removed,
51  * or modified by applications.
52  * The following attributes are automatically placed in each Provider object:
53  * <table cellspacing=4>
54  * <tr><th>Name</th><th>Value</th>
55  * <tr><td><code>Provider.id name</code></td>
56   * <td><code>String.valueOf(provider.getName())</code></td>
57  * <tr><td><code>Provider.id version</code></td>
58  * <td><code>String.valueOf(provider.getVersion())</code></td>
59  * <tr><td><code>Provider.id info</code></td>
60        <td><code>String.valueOf(provider.getInfo())</code></td>
61  * <tr><td><code>Provider.id className</code></td>
62  * <td><code>provider.getClass().getName()</code></td>
63  * </table>
64  *
65  * @version 1.64, 04/08/05
66  * @author Benjamin Renaud
67  * @author Andreas Sterbenz
68  */

69 public abstract class Provider extends Properties {
70
71     // Declare serialVersionUID to be compatible with JDK1.1
72
static final long serialVersionUID = -4298000515446427739L;
73
74     private static final sun.security.util.Debug debug =
75         sun.security.util.Debug.getInstance
76         ("provider", "Provider");
77
78     /**
79      * The provider name.
80      *
81      * @serial
82      */

83     private String JavaDoc name;
84
85     /**
86      * A description of the provider and its services.
87      *
88      * @serial
89      */

90     private String JavaDoc info;
91
92     /**
93      * The provider version number.
94      *
95      * @serial
96      */

97     private double version;
98
99
100     private transient Set entrySet = null;
101     private transient int entrySetCallCount = 0;
102
103
104     /**
105      * Constructs a provider with the specified name, version number,
106      * and information.
107      *
108      * @param name the provider name.
109      *
110      * @param version the provider version number.
111      *
112      * @param info a description of the provider and its services.
113      */

114     protected Provider(String JavaDoc name, double version, String JavaDoc info) {
115     this.name = name;
116     this.version = version;
117     this.info = info;
118     putId();
119     }
120
121     /**
122      * Returns the name of this provider.
123      *
124      * @return the name of this provider.
125      */

126     public String JavaDoc getName() {
127     return name;
128     }
129
130     /**
131      * Returns the version number for this provider.
132      *
133      * @return the version number for this provider.
134      */

135     public double getVersion() {
136     return version;
137     }
138
139     /**
140      * Returns a human-readable description of the provider and its
141      * services. This may return an HTML page, with relevant links.
142      *
143      * @return a description of the provider and its services.
144      */

145     public String JavaDoc getInfo() {
146     return info;
147     }
148
149     /**
150      * Returns a string with the name and the version number
151      * of this provider.
152      *
153      * @return the string with the name and the version number
154      * for this provider.
155      */

156     public String JavaDoc toString() {
157     return name + " version " + version;
158     }
159
160     /*
161      * override the following methods to ensure that provider
162      * information can only be changed if the caller has the appropriate
163      * permissions.
164      */

165
166     /**
167      * Clears this provider so that it no longer contains the properties
168      * used to look up facilities implemented by the provider.
169      *
170      * <p>First, if there is a security manager, its
171      * <code>checkSecurityAccess</code> method is called with the string
172      * <code>"clearProviderProperties."+name</code> (where <code>name</code>
173      * is the provider name) to see if it's ok to clear this provider.
174      * If the default implementation of <code>checkSecurityAccess</code>
175      * is used (that is, that method is not overriden), then this results in
176      * a call to the security manager's <code>checkPermission</code> method
177      * with a <code>SecurityPermission("clearProviderProperties."+name)</code>
178      * permission.
179      *
180      * @throws SecurityException
181      * if a security manager exists and its <code>{@link
182      * java.lang.SecurityManager#checkSecurityAccess}</code> method
183      * denies access to clear this provider
184      *
185      * @since 1.2
186      */

187     public synchronized void clear() {
188     check("clearProviderProperties."+name);
189     if (debug != null) {
190         debug.println("Remove " + name + " provider properties");
191     }
192     implClear();
193     }
194
195     /**
196      * Reads a property list (key and element pairs) from the input stream.
197      *
198      * @param inStream the input stream.
199      * @exception IOException if an error occurred when reading from the
200      * input stream.
201      * @see java.util.Properties#load
202      */

203     public synchronized void load(InputStream inStream) throws IOException {
204     check("putProviderProperty."+name);
205         if (debug != null) {
206             debug.println("Load " + name + " provider properties");
207         }
208     Properties tempProperties = new Properties();
209     tempProperties.load(inStream);
210     implPutAll(tempProperties);
211     }
212
213     /**
214      * Copies all of the mappings from the specified Map to this provider.
215      * These mappings will replace any properties that this provider had
216      * for any of the keys currently in the specified Map.
217      *
218      * @since 1.2
219      */

220     public synchronized void putAll(Map<?,?> t) {
221     check("putProviderProperty."+name);
222         if (debug != null) {
223             debug.println("Put all " + name + " provider properties");
224         }
225     implPutAll(t);
226     }
227     
228     /**
229      * Returns an unmodifiable Set view of the property entries contained
230      * in this Provider.
231      *
232      * @see java.util.Map.Entry
233      * @since 1.2
234      */

235     public synchronized Set<Map.Entry<Object JavaDoc,Object JavaDoc>> entrySet() {
236     if (entrySet == null) {
237         if (entrySetCallCount++ == 0) // Initial call
238
entrySet = Collections.unmodifiableMap(this).entrySet();
239         else
240         return super.entrySet(); // Recursive call
241
}
242     
243     // This exception will be thrown if the implementation of
244
// Collections.unmodifiableMap.entrySet() is changed such that it
245
// no longer calls entrySet() on the backing Map. (Provider's
246
// entrySet implementation depends on this "implementation detail",
247
// which is unlikely to change.
248
if (entrySetCallCount != 2)
249         throw new RuntimeException JavaDoc("Internal error.");
250     
251     return entrySet;
252     }
253     
254     /**
255      * Returns an unmodifiable Set view of the property keys contained in
256      * this provider.
257      *
258      * @since 1.2
259      */

260     public Set<Object JavaDoc> keySet() {
261     return Collections.unmodifiableSet(super.keySet());
262     }
263
264     /**
265      * Returns an unmodifiable Collection view of the property values
266      * contained in this provider.
267      *
268      * @since 1.2
269      */

270     public Collection<Object JavaDoc> values() {
271     return Collections.unmodifiableCollection(super.values());
272     }
273
274     /**
275      * Sets the <code>key</code> property to have the specified
276      * <code>value</code>.
277      *
278      * <p>First, if there is a security manager, its
279      * <code>checkSecurityAccess</code> method is called with the string
280      * <code>"putProviderProperty."+name</code>, where <code>name</code> is the
281      * provider name, to see if it's ok to set this provider's property values.
282      * If the default implementation of <code>checkSecurityAccess</code>
283      * is used (that is, that method is not overriden), then this results in
284      * a call to the security manager's <code>checkPermission</code> method
285      * with a <code>SecurityPermission("putProviderProperty."+name)</code>
286      * permission.
287      *
288      * @param key the property key.
289      *
290      * @param value the property value.
291      *
292      * @return the previous value of the specified property
293      * (<code>key</code>), or null if it did not have one.
294      *
295      * @throws SecurityException
296      * if a security manager exists and its <code>{@link
297      * java.lang.SecurityManager#checkSecurityAccess}</code> method
298      * denies access to set property values.
299      *
300      * @since 1.2
301      */

302     public synchronized Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
303     check("putProviderProperty."+name);
304         if (debug != null) {
305             debug.println("Set " + name + " provider property [" +
306               key + "/" + value +"]");
307         }
308     return implPut(key, value);
309     }
310
311     /**
312      * Removes the <code>key</code> property (and its corresponding
313      * <code>value</code>).
314      *
315      * <p>First, if there is a security manager, its
316      * <code>checkSecurityAccess</code> method is called with the string
317      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
318      * the provider name, to see if it's ok to remove this provider's
319      * properties. If the default implementation of
320      * <code>checkSecurityAccess</code> is used (that is, that method is not
321      * overriden), then this results in a call to the security manager's
322      * <code>checkPermission</code> method with a
323      * <code>SecurityPermission("removeProviderProperty."+name)</code>
324      * permission.
325      *
326      * @param key the key for the property to be removed.
327      *
328      * @return the value to which the key had been mapped,
329      * or null if the key did not have a mapping.
330      *
331      * @throws SecurityException
332      * if a security manager exists and its <code>{@link
333      * java.lang.SecurityManager#checkSecurityAccess}</code> method
334      * denies access to remove this provider's properties.
335      *
336      * @since 1.2
337      */

338     public synchronized Object JavaDoc remove(Object JavaDoc key) {
339     check("removeProviderProperty."+name);
340         if (debug != null) {
341             debug.println("Remove " + name + " provider property " + key);
342         }
343     return implRemove(key);
344     }
345
346     private static void check(String JavaDoc directive) {
347         SecurityManager JavaDoc security = System.getSecurityManager();
348         if (security != null) {
349             security.checkSecurityAccess(directive);
350         }
351     }
352     
353     // legacy properties changed since last call to any services method?
354
private transient boolean legacyChanged;
355     // serviceMap changed since last call to getServices()
356
private transient boolean servicesChanged;
357     
358     // Map<String,String>
359
private transient Map<String JavaDoc,String JavaDoc> legacyStrings;
360     
361     // Map<ServiceKey,Service>
362
// used for services added via putService(), initialized on demand
363
private transient Map<ServiceKey,Service> serviceMap;
364
365     // Map<ServiceKey,Service>
366
// used for services added via legacy methods, init on demand
367
private transient Map<ServiceKey,Service> legacyMap;
368     
369     // Set<Service>
370
// set of all services. initialized on demand, cleared on modification
371
private transient Set<Service> serviceSet;
372     
373     // register the id attributes for this provider
374
// this is to ensure that equals() and hashCode() do not incorrectly
375
// report to different provider objects as the same
376
private void putId() {
377     // note: name and info may be null
378
super.put("Provider.id name", String.valueOf(name));
379     super.put("Provider.id version", String.valueOf(version));
380     super.put("Provider.id info", String.valueOf(info));
381     super.put("Provider.id className", this.getClass().getName());
382     }
383
384     /**
385      * Copies all of the mappings from the specified Map to this provider.
386      * Internal method to be called AFTER the security check has been
387      * performed.
388      */

389     private void implPutAll(Map t) {
390     for (Map.Entry e : ((Map<?,?>)t).entrySet()) {
391         implPut(e.getKey(), e.getValue());
392     }
393     }
394     
395     private Object JavaDoc implRemove(Object JavaDoc key) {
396     if (key instanceof String JavaDoc) {
397         String JavaDoc keyString = (String JavaDoc)key;
398         if (keyString.startsWith("Provider.")) {
399         return null;
400         }
401         legacyChanged = true;
402         if (legacyStrings == null) {
403         legacyStrings = new LinkedHashMap<String JavaDoc,String JavaDoc>();
404         }
405         legacyStrings.remove(keyString);
406     }
407     return super.remove(key);
408     }
409     
410     private Object JavaDoc implPut(Object JavaDoc key, Object JavaDoc value) {
411     if ((key instanceof String JavaDoc) && (value instanceof String JavaDoc)) {
412         String JavaDoc keyString = (String JavaDoc)key;
413         if (keyString.startsWith("Provider.")) {
414         return null;
415         }
416         legacyChanged = true;
417         if (legacyStrings == null) {
418         legacyStrings = new LinkedHashMap<String JavaDoc,String JavaDoc>();
419         }
420         legacyStrings.put(keyString, (String JavaDoc)value);
421     }
422     return super.put(key, value);
423     }
424     
425     private void implClear() {
426     super.clear();
427     putId();
428     if (legacyStrings != null) {
429         legacyStrings.clear();
430     }
431     if (legacyMap != null) {
432         legacyMap.clear();
433     }
434     if (serviceMap != null) {
435         serviceMap.clear();
436     }
437     legacyChanged = false;
438     servicesChanged = false;
439     serviceSet = null;
440     }
441     
442     // used as key in the serviceMap and legacyMap HashMaps
443
private static class ServiceKey {
444     private final String JavaDoc type;
445     private final String JavaDoc algorithm;
446     private final String JavaDoc originalAlgorithm;
447     private ServiceKey(String JavaDoc type, String JavaDoc algorithm, boolean intern) {
448         this.type = type;
449         this.originalAlgorithm = algorithm;
450         algorithm = algorithm.toUpperCase(ENGLISH);
451         this.algorithm = intern ? algorithm.intern() : algorithm;
452     }
453     public int hashCode() {
454         return type.hashCode() + algorithm.hashCode();
455     }
456     public boolean equals(Object JavaDoc obj) {
457         if (this == obj) {
458         return true;
459         }
460         if (obj instanceof ServiceKey == false) {
461         return false;
462         }
463         ServiceKey other = (ServiceKey)obj;
464         return this.type.equals(other.type)
465             && this.algorithm.equals(other.algorithm);
466     }
467     boolean matches(String JavaDoc type, String JavaDoc algorithm) {
468         return (this.type == type) && (this.originalAlgorithm == algorithm);
469     }
470     }
471
472     /**
473      * Ensure all the legacy String properties are fully parsed into
474      * service objects.
475      */

476     private void ensureLegacyParsed() {
477     if ((legacyChanged == false) || (legacyStrings == null)) {
478         return;
479     }
480     serviceSet = null;
481     if (legacyMap == null) {
482         legacyMap = new LinkedHashMap<ServiceKey,Service>();
483     } else {
484         legacyMap.clear();
485     }
486     for (Map.Entry<String JavaDoc,String JavaDoc> entry : legacyStrings.entrySet()) {
487         parseLegacyPut(entry.getKey(), entry.getValue());
488     }
489     removeInvalidServices(legacyMap);
490     legacyChanged = false;
491     }
492     
493     /**
494      * Remove all invalid services from the Map. Invalid services can only
495      * occur if the legacy properties are inconsistent or incomplete.
496      */

497     private void removeInvalidServices(Map<ServiceKey,Service> map) {
498     for (Iterator t = map.entrySet().iterator(); t.hasNext(); ) {
499         Map.Entry entry = (Map.Entry)t.next();
500         Service s = (Service)entry.getValue();
501         if (s.isValid() == false) {
502         t.remove();
503         }
504     }
505     }
506     
507     private String JavaDoc[] getTypeAndAlgorithm(String JavaDoc key) {
508     int i = key.indexOf(".");
509     if (i < 1) {
510         if (debug != null) {
511         debug.println("Ignoring invalid entry in provider "
512             + name + ":" + key);
513         }
514         return null;
515     }
516     String JavaDoc type = key.substring(0, i);
517     String JavaDoc alg = key.substring(i + 1);
518     return new String JavaDoc[] {type, alg};
519     }
520     
521     private final static String JavaDoc ALIAS_PREFIX = "Alg.Alias.";
522     private final static String JavaDoc ALIAS_PREFIX_LOWER = "alg.alias.";
523     private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
524     
525     private void parseLegacyPut(String JavaDoc name, String JavaDoc value) {
526     if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
527         // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
528
// aliasKey ~ MessageDigest.SHA
529
String JavaDoc stdAlg = value;
530         String JavaDoc aliasKey = name.substring(ALIAS_LENGTH);
531         String JavaDoc[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
532         if (typeAndAlg == null) {
533         return;
534         }
535         String JavaDoc type = getEngineName(typeAndAlg[0]);
536         String JavaDoc aliasAlg = typeAndAlg[1].intern();
537         ServiceKey key = new ServiceKey(type, stdAlg, true);
538         Service s = (Service)legacyMap.get(key);
539         if (s == null) {
540         s = new Service(this);
541         s.type = type;
542         s.algorithm = stdAlg;
543         legacyMap.put(key, s);
544         }
545         legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
546         s.addAlias(aliasAlg);
547     } else {
548         String JavaDoc[] typeAndAlg = getTypeAndAlgorithm(name);
549         if (typeAndAlg == null) {
550         return;
551         }
552         int i = typeAndAlg[1].indexOf(' ');
553         if (i == -1) {
554         // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
555
String JavaDoc type = getEngineName(typeAndAlg[0]);
556         String JavaDoc stdAlg = typeAndAlg[1].intern();
557         String JavaDoc className = value;
558         ServiceKey key = new ServiceKey(type, stdAlg, true);
559         Service s = (Service)legacyMap.get(key);
560         if (s == null) {
561             s = new Service(this);
562             s.type = type;
563             s.algorithm = stdAlg;
564             legacyMap.put(key, s);
565         }
566         s.className = className;
567         } else { // attribute
568
// e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
569
String JavaDoc attributeValue = value;
570         String JavaDoc type = getEngineName(typeAndAlg[0]);
571         String JavaDoc attributeString = typeAndAlg[1];
572         String JavaDoc stdAlg = attributeString.substring(0, i).intern();
573         String JavaDoc attributeName = attributeString.substring(i + 1);
574         // kill additional spaces
575
while (attributeName.startsWith(" ")) {
576             attributeName = attributeName.substring(1);
577         }
578         attributeName = attributeName.intern();
579         ServiceKey key = new ServiceKey(type, stdAlg, true);
580         Service s = (Service)legacyMap.get(key);
581         if (s == null) {
582             s = new Service(this);
583             s.type = type;
584             s.algorithm = stdAlg;
585             legacyMap.put(key, s);
586         }
587         s.addAttribute(attributeName, attributeValue);
588         }
589     }
590     }
591     
592     /**
593      * Get the service describing this Provider's implementation of the
594      * specified type of this algorithm or alias. If no such
595      * implementation exists, this method returns null. If there are two
596      * matching services, one added to this provider using
597      * {@link #putService putService()} and one added via {@link #put put()},
598      * the service added via {@link #putService putService()} is returned.
599      *
600      * @param type the type of {@link Service service} requested
601      * (for example, <code>MessageDigest</code>)
602      * @param algorithm the case insensitive algorithm name (or alternate
603      * alias) of the service requested (for example, <code>SHA-1</code>)
604      *
605      * @return the service describing this Provider's matching service
606      * or null if no such service exists
607      *
608      * @throws NullPointerException if type or algorithm is null
609      *
610      * @since 1.5
611      */

612     public synchronized Service getService(String JavaDoc type, String JavaDoc algorithm) {
613     // avoid allocating a new key object if possible
614
ServiceKey key = previousKey;
615     if (key.matches(type, algorithm) == false) {
616         key = new ServiceKey(type, algorithm, false);
617         previousKey = key;
618     }
619     if (serviceMap != null) {
620         Service service = serviceMap.get(key);
621         if (service != null) {
622         return service;
623         }
624     }
625     ensureLegacyParsed();
626     return (legacyMap != null) ? legacyMap.get(key) : null;
627     }
628     
629     // ServiceKey from previous getService() call
630
// by re-using it if possible we avoid allocating a new object
631
// and the toUpperCase() call.
632
// re-use will occur e.g. as the framework traverses the provider
633
// list and queries each provider with the same values until it finds
634
// a matching service
635
private static volatile ServiceKey previousKey =
636                         new ServiceKey("", "", false);
637     
638     /**
639      * Get an unmodifiable Set of all services supported by
640      * this Provider.
641      *
642      * @return an unmodifiable Set of all services supported by
643      * this Provider
644      *
645      * @since 1.5
646      */

647     public synchronized Set<Service> getServices() {
648     if (legacyChanged || servicesChanged) {
649         serviceSet = null;
650     } else if (serviceSet != null) {
651         return serviceSet;
652     }
653     ensureLegacyParsed();
654     serviceSet = new LinkedHashSet<Service>();
655     if (serviceMap != null) {
656         serviceSet.addAll(serviceMap.values());
657     }
658     if (legacyMap != null) {
659         serviceSet.addAll(legacyMap.values());
660     }
661     servicesChanged = false;
662     return serviceSet;
663     }
664
665     /**
666      * Add a service. If a service of the same type with the same algorithm
667      * name exists and it was added using {@link #putService putService()},
668      * it is replaced by the new service.
669      * This method also places information about this service
670      * in the provider's Hashtable values in the format described in the
671      * <a HREF="../../../guide/security/CryptoSpec.html">
672      * Java Cryptography Architecture API Specification &amp; Reference </a>.
673      *
674      * <p>Also, if there is a security manager, its
675      * <code>checkSecurityAccess</code> method is called with the string
676      * <code>"putProviderProperty."+name</code>, where <code>name</code> is
677      * the provider name, to see if it's ok to set this provider's property
678      * values. If the default implementation of <code>checkSecurityAccess</code>
679      * is used (that is, that method is not overriden), then this results in
680      * a call to the security manager's <code>checkPermission</code> method with
681      * a <code>SecurityPermission("putProviderProperty."+name)</code>
682      * permission.
683      *
684      * @param s the Service to add
685      *
686      * @throws SecurityException
687      * if a security manager exists and its <code>{@link
688      * java.lang.SecurityManager#checkSecurityAccess}</code> method denies
689      * access to set property values.
690      * @throws NullPointerException if s is null
691      *
692      * @since 1.5
693      */

694     protected synchronized void putService(Service s) {
695     check("putProviderProperty." + name);
696     if (debug != null) {
697             debug.println(name + ".putService(): " + s);
698     }
699     if (s == null) {
700         throw new NullPointerException JavaDoc();
701     }
702     if (serviceMap == null) {
703         serviceMap = new LinkedHashMap<ServiceKey,Service>();
704     }
705     servicesChanged = true;
706     String JavaDoc type = s.getType();
707     String JavaDoc algorithm = s.getAlgorithm();
708     ServiceKey key = new ServiceKey(type, algorithm, true);
709     // remove existing service
710
implRemoveService(serviceMap.get(key));
711     serviceMap.put(key, s);
712     for (String JavaDoc alias : s.getAliases()) {
713         serviceMap.put(new ServiceKey(type, alias, true), s);
714     }
715     putPropertyStrings(s);
716     }
717     
718     /**
719      * Put the string properties for this Service in this Provider's
720      * Hashtable.
721      */

722     private void putPropertyStrings(Service s) {
723     String JavaDoc type = s.getType();
724     String JavaDoc algorithm = s.getAlgorithm();
725     // use super() to avoid permission check and other processing
726
super.put(type + "." + algorithm, s.getClassName());
727     for (String JavaDoc alias : s.getAliases()) {
728         super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
729     }
730     for (Map.Entry<UString,String JavaDoc> entry : s.attributes.entrySet()) {
731         String JavaDoc key = type + "." + algorithm + " " + entry.getKey();
732         super.put(key, entry.getValue());
733     }
734     }
735
736     /**
737      * Remove the string properties for this Service from this Provider's
738      * Hashtable.
739      */

740     private void removePropertyStrings(Service s) {
741     String JavaDoc type = s.getType();
742     String JavaDoc algorithm = s.getAlgorithm();
743     // use super() to avoid permission check and other processing
744
super.remove(type + "." + algorithm);
745     for (String JavaDoc alias : s.getAliases()) {
746         super.remove(ALIAS_PREFIX + type + "." + alias);
747     }
748     for (Map.Entry<UString,String JavaDoc> entry : s.attributes.entrySet()) {
749         String JavaDoc key = type + "." + algorithm + " " + entry.getKey();
750         super.remove(key);
751     }
752     }
753
754     /**
755      * Remove a service previously added using
756      * {@link #putService putService()}. The specified service is removed from
757      * this provider. It will no longer be returned by
758      * {@link #getService getService()} and its information will be removed
759      * from this provider's Hashtable.
760      *
761      * <p>Also, if there is a security manager, its
762      * <code>checkSecurityAccess</code> method is called with the string
763      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
764      * the provider name, to see if it's ok to remove this provider's
765      * properties. If the default implementation of
766      * <code>checkSecurityAccess</code> is used (that is, that method is not
767      * overriden), then this results in a call to the security manager's
768      * <code>checkPermission</code> method with a
769      * <code>SecurityPermission("removeProviderProperty."+name)</code>
770      * permission.
771      *
772      * @param s the Service to be removed
773      *
774      * @throws SecurityException
775      * if a security manager exists and its <code>{@link
776      * java.lang.SecurityManager#checkSecurityAccess}</code> method denies
777      * access to remove this provider's properties.
778      * @throws NullPointerException if s is null
779      *
780      * @since 1.5
781      */

782     protected synchronized void removeService(Service s) {
783     check("removeProviderProperty." + name);
784         if (debug != null) {
785             debug.println(name + ".removeService(): " + s);
786         }
787     if (s == null) {
788         throw new NullPointerException JavaDoc();
789     }
790     implRemoveService(s);
791     }
792     
793     private void implRemoveService(Service s) {
794     if ((s == null) || (serviceMap == null)) {
795         return;
796     }
797     String JavaDoc type = s.getType();
798     String JavaDoc algorithm = s.getAlgorithm();
799     ServiceKey key = new ServiceKey(type, algorithm, false);
800     Service oldService = serviceMap.get(key);
801     if (s != oldService) {
802         return;
803     }
804     servicesChanged = true;
805     serviceMap.remove(key);
806     for (String JavaDoc alias : s.getAliases()) {
807         serviceMap.remove(new ServiceKey(type, alias, false));
808     }
809     removePropertyStrings(s);
810     }
811
812     // Wrapped String that behaves in a case insensitive way for equals/hashCode
813
private static class UString {
814     final String JavaDoc string;
815     final String JavaDoc lowerString;
816     
817     UString(String JavaDoc s) {
818         this.string = s;
819         this.lowerString = s.toLowerCase(ENGLISH);
820     }
821     
822     public int hashCode() {
823         return lowerString.hashCode();
824     }
825     
826     public boolean equals(Object JavaDoc obj) {
827         if (this == obj) {
828         return true;
829         }
830         if (obj instanceof UString == false) {
831         return false;
832         }
833         UString other = (UString)obj;
834         return lowerString.equals(other.lowerString);
835     }
836     
837     public String JavaDoc toString() {
838         return string;
839     }
840     }
841
842     // describe relevant properties of a type of engine
843
private static class EngineDescription {
844     final String JavaDoc name;
845     final boolean constructor;
846     final boolean supportsParameter;
847     
848     EngineDescription(String JavaDoc name, boolean constructor, boolean sp) {
849         this.name = name;
850         this.constructor = constructor;
851         this.supportsParameter = sp;
852     }
853     }
854     
855     // built in knowledge of the engine types shipped as part of the JDK
856
private static final Map<String JavaDoc,EngineDescription> knownEngines;
857     
858     private static void addEngine(String JavaDoc name, boolean cons, boolean sp) {
859     EngineDescription ed = new EngineDescription(name, cons, sp);
860     // also index by canonical name to avoid toLowerCase() for some lookups
861
knownEngines.put(name.toLowerCase(ENGLISH), ed);
862     knownEngines.put(name, ed);
863     }
864     
865     static {
866     knownEngines = new HashMap<String JavaDoc,EngineDescription>();
867     // JCA
868
addEngine("AlgorithmParameterGenerator", false, false);
869     addEngine("AlgorithmParameters", false, false);
870     addEngine("KeyFactory", false, false);
871     addEngine("KeyPairGenerator", false, false);
872     addEngine("KeyStore", false, false);
873     addEngine("MessageDigest", false, false);
874     addEngine("SecureRandom", false, false);
875     addEngine("Signature", false, true);
876     addEngine("CertificateFactory", false, false);
877     addEngine("CertPathBuilder", false, false);
878     addEngine("CertPathValidator", false, false);
879     addEngine("CertStore", true, false);
880     // JCE
881
addEngine("Cipher", false, true);
882     addEngine("ExemptionMechanism", false, false);
883     addEngine("Mac", false, true);
884     addEngine("KeyAgreement", false, true);
885     addEngine("KeyGenerator", false, false);
886     addEngine("SecretKeyFactory", false, false);
887     // JSSE
888
addEngine("KeyManagerFactory", false, false);
889     addEngine("SSLContext", false, false);
890     addEngine("TrustManagerFactory", false, false);
891     // JGSS
892
addEngine("GssApiMechanism", false, false);
893     // SASL
894
addEngine("SaslClientFactory", false, false);
895     addEngine("SaslServerFactory", false, false);
896     }
897     
898     // get the "standard" (mixed-case) engine name for arbitary case engine name
899
// if there is no known engine by that name, return s
900
private static String JavaDoc getEngineName(String JavaDoc s) {
901     // try original case first, usually correct
902
EngineDescription e = knownEngines.get(s);
903     if (e == null) {
904         e = knownEngines.get(s.toLowerCase(ENGLISH));
905     }
906     return (e == null) ? s : e.name;
907     }
908     
909     /**
910      * The description of a security service. It encapsulates the properties
911      * of a service and contains a factory method to obtain new implementation
912      * instances of this service.
913      *
914      * <p>Each service has a provider that offers the service, a type,
915      * an algorithm name, and the name of the class that implements the
916      * service. Optionally, it also includes a list of alternate algorithm
917      * names for this service (aliases) and attributes, which are a map of
918