View Javadoc
1   /*
2    * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.security;
27  
28  import java.io.*;
29  import java.util.*;
30  import static java.util.Locale.ENGLISH;
31  import java.lang.ref.*;
32  import java.lang.reflect.*;
33  import java.util.function.BiConsumer;
34  import java.util.function.BiFunction;
35  import java.util.function.Function;
36  
37  /**
38   * This class represents a "provider" for the
39   * Java Security API, where a provider implements some or all parts of
40   * Java Security. Services that a provider may implement include:
41   *
42   * <ul>
43   *
44   * <li>Algorithms (such as DSA, RSA, MD5 or SHA-1).
45   *
46   * <li>Key generation, conversion, and management facilities (such as for
47   * algorithm-specific keys).
48   *
49   *</ul>
50   *
51   * <p>Each provider has a name and a version number, and is configured
52   * in each runtime it is installed in.
53   *
54   * <p>See <a href =
55   * "../../../technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a>
56   * in the "Java Cryptography Architecture API Specification &amp; Reference"
57   * for information about how a particular type of provider, the
58   * cryptographic service provider, works and is installed. However,
59   * please note that a provider can be used to implement any security
60   * service in Java that uses a pluggable architecture with a choice
61   * of implementations that fit underneath.
62   *
63   * <p>Some provider implementations may encounter unrecoverable internal
64   * errors during their operation, for example a failure to communicate with a
65   * security token. A {@link ProviderException} should be used to indicate
66   * such errors.
67   *
68   * <p>The service type {@code Provider} is reserved for use by the
69   * security framework. Services of this type cannot be added, removed,
70   * or modified by applications.
71   * The following attributes are automatically placed in each Provider object:
72   * <table cellspacing=4>
73   * <caption><b>Attributes Automatically Placed in a Provider Object</b></caption>
74   * <tr><th>Name</th><th>Value</th>
75   * <tr><td>{@code Provider.id name}</td>
76    *    <td>{@code String.valueOf(provider.getName())}</td>
77   * <tr><td>{@code Provider.id version}</td>
78   *     <td>{@code String.valueOf(provider.getVersion())}</td>
79   * <tr><td>{@code Provider.id info}</td>
80         <td>{@code String.valueOf(provider.getInfo())}</td>
81   * <tr><td>{@code Provider.id className}</td>
82   *     <td>{@code provider.getClass().getName()}</td>
83   * </table>
84   *
85   * @author Benjamin Renaud
86   * @author Andreas Sterbenz
87   */
88  public abstract class Provider extends Properties {
89  
90      // Declare serialVersionUID to be compatible with JDK1.1
91      static final long serialVersionUID = -4298000515446427739L;
92  
93      private static final sun.security.util.Debug debug =
94          sun.security.util.Debug.getInstance
95          ("provider", "Provider");
96  
97      /**
98       * The provider name.
99       *
100      * @serial
101      */
102     private String name;
103 
104     /**
105      * A description of the provider and its services.
106      *
107      * @serial
108      */
109     private String info;
110 
111     /**
112      * The provider version number.
113      *
114      * @serial
115      */
116     private double version;
117 
118 
119     private transient Set<Map.Entry<Object,Object>> entrySet = null;
120     private transient int entrySetCallCount = 0;
121 
122     private transient boolean initialized;
123 
124     /**
125      * Constructs a provider with the specified name, version number,
126      * and information.
127      *
128      * @param name the provider name.
129      *
130      * @param version the provider version number.
131      *
132      * @param info a description of the provider and its services.
133      */
134     protected Provider(String name, double version, String info) {
135         this.name = name;
136         this.version = version;
137         this.info = info;
138         putId();
139         initialized = true;
140     }
141 
142     /**
143      * Returns the name of this provider.
144      *
145      * @return the name of this provider.
146      */
147     public String getName() {
148         return name;
149     }
150 
151     /**
152      * Returns the version number for this provider.
153      *
154      * @return the version number for this provider.
155      */
156     public double getVersion() {
157         return version;
158     }
159 
160     /**
161      * Returns a human-readable description of the provider and its
162      * services.  This may return an HTML page, with relevant links.
163      *
164      * @return a description of the provider and its services.
165      */
166     public String getInfo() {
167         return info;
168     }
169 
170     /**
171      * Returns a string with the name and the version number
172      * of this provider.
173      *
174      * @return the string with the name and the version number
175      * for this provider.
176      */
177     public String toString() {
178         return name + " version " + version;
179     }
180 
181     /*
182      * override the following methods to ensure that provider
183      * information can only be changed if the caller has the appropriate
184      * permissions.
185      */
186 
187     /**
188      * Clears this provider so that it no longer contains the properties
189      * used to look up facilities implemented by the provider.
190      *
191      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
192      * method is called with the string {@code "clearProviderProperties."+name}
193      * (where {@code name} is the provider name) to see if it's ok to clear
194      * this provider.
195      *
196      * @throws  SecurityException
197      *          if a security manager exists and its {@link
198      *          java.lang.SecurityManager#checkSecurityAccess} method
199      *          denies access to clear this provider
200      *
201      * @since 1.2
202      */
203     @Override
204     public synchronized void clear() {
205         check("clearProviderProperties."+name);
206         if (debug != null) {
207             debug.println("Remove " + name + " provider properties");
208         }
209         implClear();
210     }
211 
212     /**
213      * Reads a property list (key and element pairs) from the input stream.
214      *
215      * @param inStream   the input stream.
216      * @exception  IOException  if an error occurred when reading from the
217      *               input stream.
218      * @see java.util.Properties#load
219      */
220     @Override
221     public synchronized void load(InputStream inStream) throws IOException {
222         check("putProviderProperty."+name);
223         if (debug != null) {
224             debug.println("Load " + name + " provider properties");
225         }
226         Properties tempProperties = new Properties();
227         tempProperties.load(inStream);
228         implPutAll(tempProperties);
229     }
230 
231     /**
232      * Copies all of the mappings from the specified Map to this provider.
233      * These mappings will replace any properties that this provider had
234      * for any of the keys currently in the specified Map.
235      *
236      * @since 1.2
237      */
238     @Override
239     public synchronized void putAll(Map<?,?> t) {
240         check("putProviderProperty."+name);
241         if (debug != null) {
242             debug.println("Put all " + name + " provider properties");
243         }
244         implPutAll(t);
245     }
246 
247     /**
248      * Returns an unmodifiable Set view of the property entries contained
249      * in this Provider.
250      *
251      * @see   java.util.Map.Entry
252      * @since 1.2
253      */
254     @Override
255     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
256         checkInitialized();
257         if (entrySet == null) {
258             if (entrySetCallCount++ == 0)  // Initial call
259                 entrySet = Collections.unmodifiableMap(this).entrySet();
260             else
261                 return super.entrySet();   // Recursive call
262         }
263 
264         // This exception will be thrown if the implementation of
265         // Collections.unmodifiableMap.entrySet() is changed such that it
266         // no longer calls entrySet() on the backing Map.  (Provider's
267         // entrySet implementation depends on this "implementation detail",
268         // which is unlikely to change.
269         if (entrySetCallCount != 2)
270             throw new RuntimeException("Internal error.");
271 
272         return entrySet;
273     }
274 
275     /**
276      * Returns an unmodifiable Set view of the property keys contained in
277      * this provider.
278      *
279      * @since 1.2
280      */
281     @Override
282     public Set<Object> keySet() {
283         checkInitialized();
284         return Collections.unmodifiableSet(super.keySet());
285     }
286 
287     /**
288      * Returns an unmodifiable Collection view of the property values
289      * contained in this provider.
290      *
291      * @since 1.2
292      */
293     @Override
294     public Collection<Object> values() {
295         checkInitialized();
296         return Collections.unmodifiableCollection(super.values());
297     }
298 
299     /**
300      * Sets the {@code key} property to have the specified
301      * {@code value}.
302      *
303      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
304      * method is called with the string {@code "putProviderProperty."+name},
305      * where {@code name} is the provider name, to see if it's ok to set this
306      * provider's property values.
307      *
308      * @throws  SecurityException
309      *          if a security manager exists and its {@link
310      *          java.lang.SecurityManager#checkSecurityAccess} method
311      *          denies access to set property values.
312      *
313      * @since 1.2
314      */
315     @Override
316     public synchronized Object put(Object key, Object value) {
317         check("putProviderProperty."+name);
318         if (debug != null) {
319             debug.println("Set " + name + " provider property [" +
320                           key + "/" + value +"]");
321         }
322         return implPut(key, value);
323     }
324 
325     /**
326      * If the specified key is not already associated with a value (or is mapped
327      * to {@code null}) associates it with the given value and returns
328      * {@code null}, else returns the current value.
329      *
330      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
331      * method is called with the string {@code "putProviderProperty."+name},
332      * where {@code name} is the provider name, to see if it's ok to set this
333      * provider's property values.
334      *
335      * @throws  SecurityException
336      *          if a security manager exists and its {@link
337      *          java.lang.SecurityManager#checkSecurityAccess} method
338      *          denies access to set property values.
339      *
340      * @since 1.8
341      */
342     @Override
343     public synchronized Object putIfAbsent(Object key, Object value) {
344         check("putProviderProperty."+name);
345         if (debug != null) {
346             debug.println("Set " + name + " provider property [" +
347                           key + "/" + value +"]");
348         }
349         return implPutIfAbsent(key, value);
350     }
351 
352     /**
353      * Removes the {@code key} property (and its corresponding
354      * {@code value}).
355      *
356      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
357      * method is called with the string {@code "removeProviderProperty."+name},
358      * where {@code name} is the provider name, to see if it's ok to remove this
359      * provider's properties.
360      *
361      * @throws  SecurityException
362      *          if a security manager exists and its {@link
363      *          java.lang.SecurityManager#checkSecurityAccess} method
364      *          denies access to remove this provider's properties.
365      *
366      * @since 1.2
367      */
368     @Override
369     public synchronized Object remove(Object key) {
370         check("removeProviderProperty."+name);
371         if (debug != null) {
372             debug.println("Remove " + name + " provider property " + key);
373         }
374         return implRemove(key);
375     }
376 
377     /**
378      * Removes the entry for the specified key only if it is currently
379      * mapped to the specified value.
380      *
381      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
382      * method is called with the string {@code "removeProviderProperty."+name},
383      * where {@code name} is the provider name, to see if it's ok to remove this
384      * provider's properties.
385      *
386      * @throws  SecurityException
387      *          if a security manager exists and its {@link
388      *          java.lang.SecurityManager#checkSecurityAccess} method
389      *          denies access to remove this provider's properties.
390      *
391      * @since 1.8
392      */
393     @Override
394     public synchronized boolean remove(Object key, Object value) {
395         check("removeProviderProperty."+name);
396         if (debug != null) {
397             debug.println("Remove " + name + " provider property " + key);
398         }
399         return implRemove(key, value);
400     }
401 
402     /**
403      * Replaces the entry for the specified key only if currently
404      * mapped to the specified value.
405      *
406      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
407      * method is called with the string {@code "putProviderProperty."+name},
408      * where {@code name} is the provider name, to see if it's ok to set this
409      * provider's property values.
410      *
411      * @throws  SecurityException
412      *          if a security manager exists and its {@link
413      *          java.lang.SecurityManager#checkSecurityAccess} method
414      *          denies access to set property values.
415      *
416      * @since 1.8
417      */
418     @Override
419     public synchronized boolean replace(Object key, Object oldValue,
420             Object newValue) {
421         check("putProviderProperty." + name);
422 
423         if (debug != null) {
424             debug.println("Replace " + name + " provider property " + key);
425         }
426         return implReplace(key, oldValue, newValue);
427     }
428 
429     /**
430      * Replaces the entry for the specified key only if it is
431      * currently mapped to some value.
432      *
433      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
434      * method is called with the string {@code "putProviderProperty."+name},
435      * where {@code name} is the provider name, to see if it's ok to set this
436      * provider's property values.
437      *
438      * @throws  SecurityException
439      *          if a security manager exists and its {@link
440      *          java.lang.SecurityManager#checkSecurityAccess} method
441      *          denies access to set property values.
442      *
443      * @since 1.8
444      */
445     @Override
446     public synchronized Object replace(Object key, Object value) {
447         check("putProviderProperty." + name);
448 
449         if (debug != null) {
450             debug.println("Replace " + name + " provider property " + key);
451         }
452         return implReplace(key, value);
453     }
454 
455     /**
456      * Replaces each entry's value with the result of invoking the given
457      * function on that entry, in the order entries are returned by an entry
458      * set iterator, until all entries have been processed or the function
459      * throws an exception.
460      *
461      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
462      * method is called with the string {@code "putProviderProperty."+name},
463      * where {@code name} is the provider name, to see if it's ok to set this
464      * provider's property values.
465      *
466      * @throws  SecurityException
467      *          if a security manager exists and its {@link
468      *          java.lang.SecurityManager#checkSecurityAccess} method
469      *          denies access to set property values.
470      *
471      * @since 1.8
472      */
473     @Override
474     public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
475         check("putProviderProperty." + name);
476 
477         if (debug != null) {
478             debug.println("ReplaceAll " + name + " provider property ");
479         }
480         implReplaceAll(function);
481     }
482 
483     /**
484      * Attempts to compute a mapping for the specified key and its
485      * current mapped value (or {@code null} if there is no current
486      * mapping).
487      *
488      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
489      * method is called with the strings {@code "putProviderProperty."+name}
490      * and {@code "removeProviderProperty."+name}, where {@code name} is the
491      * provider name, to see if it's ok to set this provider's property values
492      * and remove this provider's properties.
493      *
494      * @throws  SecurityException
495      *          if a security manager exists and its {@link
496      *          java.lang.SecurityManager#checkSecurityAccess} method
497      *          denies access to set property values or remove properties.
498      *
499      * @since 1.8
500      */
501     @Override
502     public synchronized Object compute(Object key,
503         BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
504         check("putProviderProperty." + name);
505         check("removeProviderProperty" + name);
506 
507         if (debug != null) {
508             debug.println("Compute " + name + " provider property " + key);
509         }
510         return implCompute(key, remappingFunction);
511     }
512 
513     /**
514      * If the specified key is not already associated with a value (or
515      * is mapped to {@code null}), attempts to compute its value using
516      * the given mapping function and enters it into this map unless
517      * {@code null}.
518      *
519      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
520      * method is called with the strings {@code "putProviderProperty."+name}
521      * and {@code "removeProviderProperty."+name}, where {@code name} is the
522      * provider name, to see if it's ok to set this provider's property values
523      * and remove this provider's properties.
524      *
525      * @throws  SecurityException
526      *          if a security manager exists and its {@link
527      *          java.lang.SecurityManager#checkSecurityAccess} method
528      *          denies access to set property values and remove properties.
529      *
530      * @since 1.8
531      */
532     @Override
533     public synchronized Object computeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
534         check("putProviderProperty." + name);
535         check("removeProviderProperty" + name);
536 
537         if (debug != null) {
538             debug.println("ComputeIfAbsent " + name + " provider property " +
539                     key);
540         }
541         return implComputeIfAbsent(key, mappingFunction);
542     }
543 
544     /**
545      * If the value for the specified key is present and non-null, attempts to
546      * compute a new mapping given the key and its current mapped value.
547      *
548      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
549      * method is called with the strings {@code "putProviderProperty."+name}
550      * and {@code "removeProviderProperty."+name}, where {@code name} is the
551      * provider name, to see if it's ok to set this provider's property values
552      * and remove this provider's properties.
553      *
554      * @throws  SecurityException
555      *          if a security manager exists and its {@link
556      *          java.lang.SecurityManager#checkSecurityAccess} method
557      *          denies access to set property values or remove properties.
558      *
559      * @since 1.8
560      */
561     @Override
562     public synchronized Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
563         check("putProviderProperty." + name);
564         check("removeProviderProperty" + name);
565 
566         if (debug != null) {
567             debug.println("ComputeIfPresent " + name + " provider property " +
568                     key);
569         }
570         return implComputeIfPresent(key, remappingFunction);
571     }
572 
573     /**
574      * If the specified key is not already associated with a value or is
575      * associated with null, associates it with the given value. Otherwise,
576      * replaces the value with the results of the given remapping function,
577      * or removes if the result is null. This method may be of use when
578      * combining multiple mapped values for a key.
579      *
580      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
581      * method is called with the strings {@code "putProviderProperty."+name}
582      * and {@code "removeProviderProperty."+name}, where {@code name} is the
583      * provider name, to see if it's ok to set this provider's property values
584      * and remove this provider's properties.
585      *
586      * @throws  SecurityException
587      *          if a security manager exists and its {@link
588      *          java.lang.SecurityManager#checkSecurityAccess} method
589      *          denies access to set property values or remove properties.
590      *
591      * @since 1.8
592      */
593     @Override
594     public synchronized Object merge(Object key, Object value,  BiFunction<? super Object, ? super Object, ? extends Object>  remappingFunction) {
595         check("putProviderProperty." + name);
596         check("removeProviderProperty" + name);
597 
598         if (debug != null) {
599             debug.println("Merge " + name + " provider property " + key);
600         }
601         return implMerge(key, value, remappingFunction);
602     }
603 
604     // let javadoc show doc from superclass
605     @Override
606     public Object get(Object key) {
607         checkInitialized();
608         return super.get(key);
609     }
610     /**
611      * @since 1.8
612      */
613     @Override
614     public synchronized Object getOrDefault(Object key, Object defaultValue) {
615         checkInitialized();
616         return super.getOrDefault(key, defaultValue);
617     }
618 
619     /**
620      * @since 1.8
621      */
622     @Override
623     public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
624         checkInitialized();
625         super.forEach(action);
626     }
627 
628     // let javadoc show doc from superclass
629     @Override
630     public Enumeration<Object> keys() {
631         checkInitialized();
632         return super.keys();
633     }
634 
635     // let javadoc show doc from superclass
636     @Override
637     public Enumeration<Object> elements() {
638         checkInitialized();
639         return super.elements();
640     }
641 
642     // let javadoc show doc from superclass
643     public String getProperty(String key) {
644         checkInitialized();
645         return super.getProperty(key);
646     }
647 
648     private void checkInitialized() {
649         if (!initialized) {
650             throw new IllegalStateException();
651         }
652     }
653 
654     private void check(String directive) {
655         checkInitialized();
656         SecurityManager security = System.getSecurityManager();
657         if (security != null) {
658             security.checkSecurityAccess(directive);
659         }
660     }
661 
662     // legacy properties changed since last call to any services method?
663     private transient boolean legacyChanged;
664     // serviceMap changed since last call to getServices()
665     private transient boolean servicesChanged;
666 
667     // Map<String,String>
668     private transient Map<String,String> legacyStrings;
669 
670     // Map<ServiceKey,Service>
671     // used for services added via putService(), initialized on demand
672     private transient Map<ServiceKey,Service> serviceMap;
673 
674     // Map<ServiceKey,Service>
675     // used for services added via legacy methods, init on demand
676     private transient Map<ServiceKey,Service> legacyMap;
677 
678     // Set<Service>
679     // Unmodifiable set of all services. Initialized on demand.
680     private transient Set<Service> serviceSet;
681 
682     // register the id attributes for this provider
683     // this is to ensure that equals() and hashCode() do not incorrectly
684     // report to different provider objects as the same
685     private void putId() {
686         // note: name and info may be null
687         super.put("Provider.id name", String.valueOf(name));
688         super.put("Provider.id version", String.valueOf(version));
689         super.put("Provider.id info", String.valueOf(info));
690         super.put("Provider.id className", this.getClass().getName());
691     }
692 
693     private void readObject(ObjectInputStream in)
694                 throws IOException, ClassNotFoundException {
695         Map<Object,Object> copy = new HashMap<>();
696         for (Map.Entry<Object,Object> entry : super.entrySet()) {
697             copy.put(entry.getKey(), entry.getValue());
698         }
699         defaults = null;
700         in.defaultReadObject();
701         implClear();
702         initialized = true;
703         putAll(copy);
704     }
705 
706     private boolean checkLegacy(Object key) {
707         String keyString = (String)key;
708         if (keyString.startsWith("Provider.")) {
709             return false;
710         }
711 
712         legacyChanged = true;
713         if (legacyStrings == null) {
714             legacyStrings = new LinkedHashMap<String,String>();
715         }
716         return true;
717     }
718 
719     /**
720      * Copies all of the mappings from the specified Map to this provider.
721      * Internal method to be called AFTER the security check has been
722      * performed.
723      */
724     private void implPutAll(Map<?,?> t) {
725         for (Map.Entry<?,?> e : t.entrySet()) {
726             implPut(e.getKey(), e.getValue());
727         }
728     }
729 
730     private Object implRemove(Object key) {
731         if (key instanceof String) {
732             if (!checkLegacy(key)) {
733                 return null;
734             }
735             legacyStrings.remove((String)key);
736         }
737         return super.remove(key);
738     }
739 
740     private boolean implRemove(Object key, Object value) {
741         if (key instanceof String && value instanceof String) {
742             if (!checkLegacy(key)) {
743                 return false;
744             }
745             legacyStrings.remove((String)key, value);
746         }
747         return super.remove(key, value);
748     }
749 
750     private boolean implReplace(Object key, Object oldValue, Object newValue) {
751         if ((key instanceof String) && (oldValue instanceof String) &&
752                 (newValue instanceof String)) {
753             if (!checkLegacy(key)) {
754                 return false;
755             }
756             legacyStrings.replace((String)key, (String)oldValue,
757                     (String)newValue);
758         }
759         return super.replace(key, oldValue, newValue);
760     }
761 
762     private Object implReplace(Object key, Object value) {
763         if ((key instanceof String) && (value instanceof String)) {
764             if (!checkLegacy(key)) {
765                 return null;
766             }
767             legacyStrings.replace((String)key, (String)value);
768         }
769         return super.replace(key, value);
770     }
771 
772     private void implReplaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
773         legacyChanged = true;
774         if (legacyStrings == null) {
775             legacyStrings = new LinkedHashMap<String,String>();
776         } else {
777             legacyStrings.replaceAll((BiFunction<? super String, ? super String, ? extends String>) function);
778         }
779         super.replaceAll(function);
780     }
781 
782 
783     private Object implMerge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
784         if ((key instanceof String) && (value instanceof String)) {
785             if (!checkLegacy(key)) {
786                 return null;
787             }
788             legacyStrings.merge((String)key, (String)value,
789                     (BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
790         }
791         return super.merge(key, value, remappingFunction);
792     }
793 
794     private Object implCompute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
795         if (key instanceof String) {
796             if (!checkLegacy(key)) {
797                 return null;
798             }
799             legacyStrings.computeIfAbsent((String) key,
800                     (Function<? super String, ? extends String>) remappingFunction);
801         }
802         return super.compute(key, remappingFunction);
803     }
804 
805     private Object implComputeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
806         if (key instanceof String) {
807             if (!checkLegacy(key)) {
808                 return null;
809             }
810             legacyStrings.computeIfAbsent((String) key,
811                     (Function<? super String, ? extends String>) mappingFunction);
812         }
813         return super.computeIfAbsent(key, mappingFunction);
814     }
815 
816     private Object implComputeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
817         if (key instanceof String) {
818             if (!checkLegacy(key)) {
819                 return null;
820             }
821             legacyStrings.computeIfPresent((String) key,
822                     (BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
823         }
824         return super.computeIfPresent(key, remappingFunction);
825     }
826 
827     private Object implPut(Object key, Object value) {
828         if ((key instanceof String) && (value instanceof String)) {
829             if (!checkLegacy(key)) {
830                 return null;
831             }
832             legacyStrings.put((String)key, (String)value);
833         }
834         return super.put(key, value);
835     }
836 
837     private Object implPutIfAbsent(Object key, Object value) {
838         if ((key instanceof String) && (value instanceof String)) {
839             if (!checkLegacy(key)) {
840                 return null;
841             }
842             legacyStrings.putIfAbsent((String)key, (String)value);
843         }
844         return super.putIfAbsent(key, value);
845     }
846 
847     private void implClear() {
848         if (legacyStrings != null) {
849             legacyStrings.clear();
850         }
851         if (legacyMap != null) {
852             legacyMap.clear();
853         }
854         if (serviceMap != null) {
855             serviceMap.clear();
856         }
857         legacyChanged = false;
858         servicesChanged = false;
859         serviceSet = null;
860         super.clear();
861         putId();
862     }
863 
864     // used as key in the serviceMap and legacyMap HashMaps
865     private static class ServiceKey {
866         private final String type;
867         private final String algorithm;
868         private final String originalAlgorithm;
869         private ServiceKey(String type, String algorithm, boolean intern) {
870             this.type = type;
871             this.originalAlgorithm = algorithm;
872             algorithm = algorithm.toUpperCase(ENGLISH);
873             this.algorithm = intern ? algorithm.intern() : algorithm;
874         }
875         public int hashCode() {
876             return type.hashCode() + algorithm.hashCode();
877         }
878         public boolean equals(Object obj) {
879             if (this == obj) {
880                 return true;
881             }
882             if (obj instanceof ServiceKey == false) {
883                 return false;
884             }
885             ServiceKey other = (ServiceKey)obj;
886             return this.type.equals(other.type)
887                 && this.algorithm.equals(other.algorithm);
888         }
889         boolean matches(String type, String algorithm) {
890             return (this.type == type) && (this.originalAlgorithm == algorithm);
891         }
892     }
893 
894     /**
895      * Ensure all the legacy String properties are fully parsed into
896      * service objects.
897      */
898     private void ensureLegacyParsed() {
899         if ((legacyChanged == false) || (legacyStrings == null)) {
900             return;
901         }
902         serviceSet = null;
903         if (legacyMap == null) {
904             legacyMap = new LinkedHashMap<ServiceKey,Service>();
905         } else {
906             legacyMap.clear();
907         }
908         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
909             parseLegacyPut(entry.getKey(), entry.getValue());
910         }
911         removeInvalidServices(legacyMap);
912         legacyChanged = false;
913     }
914 
915     /**
916      * Remove all invalid services from the Map. Invalid services can only
917      * occur if the legacy properties are inconsistent or incomplete.
918      */
919     private void removeInvalidServices(Map<ServiceKey,Service> map) {
920         for (Iterator<Map.Entry<ServiceKey, Service>> t =
921                 map.entrySet().iterator(); t.hasNext(); ) {
922             Service s = t.next().getValue();
923             if (s.isValid() == false) {
924                 t.remove();
925             }
926         }
927     }
928 
929     private String[] getTypeAndAlgorithm(String key) {
930         int i = key.indexOf(".");
931         if (i < 1) {
932             if (debug != null) {
933                 debug.println("Ignoring invalid entry in provider "
934                         + name + ":" + key);
935             }
936             return null;
937         }
938         String type = key.substring(0, i);
939         String alg = key.substring(i + 1);
940         return new String[] {type, alg};
941     }
942 
943     private final static String ALIAS_PREFIX = "Alg.Alias.";
944     private final static String ALIAS_PREFIX_LOWER = "alg.alias.";
945     private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
946 
947     private void parseLegacyPut(String name, String value) {
948         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
949             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
950             // aliasKey ~ MessageDigest.SHA
951             String stdAlg = value;
952             String aliasKey = name.substring(ALIAS_LENGTH);
953             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
954             if (typeAndAlg == null) {
955                 return;
956             }
957             String type = getEngineName(typeAndAlg[0]);
958             String aliasAlg = typeAndAlg[1].intern();
959             ServiceKey key = new ServiceKey(type, stdAlg, true);
960             Service s = legacyMap.get(key);
961             if (s == null) {
962                 s = new Service(this);
963                 s.type = type;
964                 s.algorithm = stdAlg;
965                 legacyMap.put(key, s);
966             }
967             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
968             s.addAlias(aliasAlg);
969         } else {
970             String[] typeAndAlg = getTypeAndAlgorithm(name);
971             if (typeAndAlg == null) {
972                 return;
973             }
974             int i = typeAndAlg[1].indexOf(' ');
975             if (i == -1) {
976                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
977                 String type = getEngineName(typeAndAlg[0]);
978                 String stdAlg = typeAndAlg[1].intern();
979                 String className = value;
980                 ServiceKey key = new ServiceKey(type, stdAlg, true);
981                 Service s = legacyMap.get(key);
982                 if (s == null) {
983                     s = new Service(this);
984                     s.type = type;
985                     s.algorithm = stdAlg;
986                     legacyMap.put(key, s);
987                 }
988                 s.className = className;
989             } else { // attribute
990                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
991                 String attributeValue = value;
992                 String type = getEngineName(typeAndAlg[0]);
993                 String attributeString = typeAndAlg[1];
994                 String stdAlg = attributeString.substring(0, i).intern();
995                 String attributeName = attributeString.substring(i + 1);
996                 // kill additional spaces
997                 while (attributeName.startsWith(" ")) {
998                     attributeName = attributeName.substring(1);
999                 }
1000                 attributeName = attributeName.intern();
1001                 ServiceKey key = new ServiceKey(type, stdAlg, true);
1002                 Service s = legacyMap.get(key);
1003                 if (s == null) {
1004                     s = new Service(this);
1005                     s.type = type;
1006                     s.algorithm = stdAlg;
1007                     legacyMap.put(key, s);
1008                 }
1009                 s.addAttribute(attributeName, attributeValue);
1010             }
1011         }
1012     }
1013 
1014     /**
1015      * Get the service describing this Provider's implementation of the
1016      * specified type of this algorithm or alias. If no such
1017      * implementation exists, this method returns null. If there are two
1018      * matching services, one added to this provider using
1019      * {@link #putService putService()} and one added via {@link #put put()},
1020      * the service added via {@link #putService putService()} is returned.
1021      *
1022      * @param type the type of {@link Service service} requested
1023      * (for example, {@code MessageDigest})
1024      * @param algorithm the case insensitive algorithm name (or alternate
1025      * alias) of the service requested (for example, {@code SHA-1})
1026      *
1027      * @return the service describing this Provider's matching service
1028      * or null if no such service exists
1029      *
1030      * @throws NullPointerException if type or algorithm is null
1031      *
1032      * @since 1.5
1033      */
1034     public synchronized Service getService(String type, String algorithm) {
1035         checkInitialized();
1036         // avoid allocating a new key object if possible
1037         ServiceKey key = previousKey;
1038         if (key.matches(type, algorithm) == false) {
1039             key = new ServiceKey(type, algorithm, false);
1040             previousKey = key;
1041         }
1042         if (serviceMap != null) {
1043             Service service = serviceMap.get(key);
1044             if (service != null) {
1045                 return service;
1046             }
1047         }
1048         ensureLegacyParsed();
1049         return (legacyMap != null) ? legacyMap.get(key) : null;
1050     }
1051 
1052     // ServiceKey from previous getService() call
1053     // by re-using it if possible we avoid allocating a new object
1054     // and the toUpperCase() call.
1055     // re-use will occur e.g. as the framework traverses the provider
1056     // list and queries each provider with the same values until it finds
1057     // a matching service
1058     private static volatile ServiceKey previousKey =
1059                                             new ServiceKey("", "", false);
1060 
1061     /**
1062      * Get an unmodifiable Set of all services supported by
1063      * this Provider.
1064      *
1065      * @return an unmodifiable Set of all services supported by
1066      * this Provider
1067      *
1068      * @since 1.5
1069      */
1070     public synchronized Set<Service> getServices() {
1071         checkInitialized();
1072         if (legacyChanged || servicesChanged) {
1073             serviceSet = null;
1074         }
1075         if (serviceSet == null) {
1076             ensureLegacyParsed();
1077             Set<Service> set = new LinkedHashSet<>();
1078             if (serviceMap != null) {
1079                 set.addAll(serviceMap.values());
1080             }
1081             if (legacyMap != null) {
1082                 set.addAll(legacyMap.values());
1083             }
1084             serviceSet = Collections.unmodifiableSet(set);
1085             servicesChanged = false;
1086         }
1087         return serviceSet;
1088     }
1089 
1090     /**
1091      * Add a service. If a service of the same type with the same algorithm
1092      * name exists and it was added using {@link #putService putService()},
1093      * it is replaced by the new service.
1094      * This method also places information about this service
1095      * in the provider's Hashtable values in the format described in the
1096      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1097      * Java Cryptography Architecture API Specification &amp; Reference </a>.
1098      *
1099      * <p>Also, if there is a security manager, its
1100      * {@code checkSecurityAccess} method is called with the string
1101      * {@code "putProviderProperty."+name}, where {@code name} is
1102      * the provider name, to see if it's ok to set this provider's property
1103      * values. If the default implementation of {@code checkSecurityAccess}
1104      * is used (that is, that method is not overriden), then this results in
1105      * a call to the security manager's {@code checkPermission} method with
1106      * a {@code SecurityPermission("putProviderProperty."+name)}
1107      * permission.
1108      *
1109      * @param s the Service to add
1110      *
1111      * @throws SecurityException
1112      *      if a security manager exists and its {@link
1113      *      java.lang.SecurityManager#checkSecurityAccess} method denies
1114      *      access to set property values.
1115      * @throws NullPointerException if s is null
1116      *
1117      * @since 1.5
1118      */
1119     protected synchronized void putService(Service s) {
1120         check("putProviderProperty." + name);
1121         if (debug != null) {
1122             debug.println(name + ".putService(): " + s);
1123         }
1124         if (s == null) {
1125             throw new NullPointerException();
1126         }
1127         if (s.getProvider() != this) {
1128             throw new IllegalArgumentException
1129                     ("service.getProvider() must match this Provider object");
1130         }
1131         if (serviceMap == null) {
1132             serviceMap = new LinkedHashMap<ServiceKey,Service>();
1133         }
1134         servicesChanged = true;
1135         String type = s.getType();
1136         String algorithm = s.getAlgorithm();
1137         ServiceKey key = new ServiceKey(type, algorithm, true);
1138         // remove existing service
1139         implRemoveService(serviceMap.get(key));
1140         serviceMap.put(key, s);
1141         for (String alias : s.getAliases()) {
1142             serviceMap.put(new ServiceKey(type, alias, true), s);
1143         }
1144         putPropertyStrings(s);
1145     }
1146 
1147     /**
1148      * Put the string properties for this Service in this Provider's
1149      * Hashtable.
1150      */
1151     private void putPropertyStrings(Service s) {
1152         String type = s.getType();
1153         String algorithm = s.getAlgorithm();
1154         // use super() to avoid permission check and other processing
1155         super.put(type + "." + algorithm, s.getClassName());
1156         for (String alias : s.getAliases()) {
1157             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
1158         }
1159         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1160             String key = type + "." + algorithm + " " + entry.getKey();
1161             super.put(key, entry.getValue());
1162         }
1163     }
1164 
1165     /**
1166      * Remove the string properties for this Service from this Provider's
1167      * Hashtable.
1168      */
1169     private void removePropertyStrings(Service s) {
1170         String type = s.getType();
1171         String algorithm = s.getAlgorithm();
1172         // use super() to avoid permission check and other processing
1173         super.remove(type + "." + algorithm);
1174         for (String alias : s.getAliases()) {
1175             super.remove(ALIAS_PREFIX + type + "." + alias);
1176         }
1177         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1178             String key = type + "." + algorithm + " " + entry.getKey();
1179             super.remove(key);
1180         }
1181     }
1182 
1183     /**
1184      * Remove a service previously added using
1185      * {@link #putService putService()}. The specified service is removed from
1186      * this provider. It will no longer be returned by
1187      * {@link #getService getService()} and its information will be removed
1188      * from this provider's Hashtable.
1189      *
1190      * <p>Also, if there is a security manager, its
1191      * {@code checkSecurityAccess} method is called with the string
1192      * {@code "removeProviderProperty."+name}, where {@code name} is
1193      * the provider name, to see if it's ok to remove this provider's
1194      * properties. If the default implementation of
1195      * {@code checkSecurityAccess} is used (that is, that method is not
1196      * overriden), then this results in a call to the security manager's
1197      * {@code checkPermission} method with a
1198      * {@code SecurityPermission("removeProviderProperty."+name)}
1199      * permission.
1200      *
1201      * @param s the Service to be removed
1202      *
1203      * @throws  SecurityException
1204      *          if a security manager exists and its {@link
1205      *          java.lang.SecurityManager#checkSecurityAccess} method denies
1206      *          access to remove this provider's properties.
1207      * @throws NullPointerException if s is null
1208      *
1209      * @since 1.5
1210      */
1211     protected synchronized void removeService(Service s) {
1212         check("removeProviderProperty." + name);
1213         if (debug != null) {
1214             debug.println(name + ".removeService(): " + s);
1215         }
1216         if (s == null) {
1217             throw new NullPointerException();
1218         }
1219         implRemoveService(s);
1220     }
1221 
1222     private void implRemoveService(Service s) {
1223         if ((s == null) || (serviceMap == null)) {
1224             return;
1225         }
1226         String type = s.getType();
1227         String algorithm = s.getAlgorithm();
1228         ServiceKey key = new ServiceKey(type, algorithm, false);
1229         Service oldService = serviceMap.get(key);
1230         if (s != oldService) {
1231             return;
1232         }
1233         servicesChanged = true;
1234         serviceMap.remove(key);
1235         for (String alias : s.getAliases()) {
1236             serviceMap.remove(new ServiceKey(type, alias, false));
1237         }
1238         removePropertyStrings(s);
1239     }
1240 
1241     // Wrapped String that behaves in a case insensitive way for equals/hashCode
1242     private static class UString {
1243         final String string;
1244         final String lowerString;
1245 
1246         UString(String s) {
1247             this.string = s;
1248             this.lowerString = s.toLowerCase(ENGLISH);
1249         }
1250 
1251         public int hashCode() {
1252             return lowerString.hashCode();
1253         }
1254 
1255         public boolean equals(Object obj) {
1256             if (this == obj) {
1257                 return true;
1258             }
1259             if (obj instanceof UString == false) {
1260                 return false;
1261             }
1262             UString other = (UString)obj;
1263             return lowerString.equals(other.lowerString);
1264         }
1265 
1266         public String toString() {
1267             return string;
1268         }
1269     }
1270 
1271     // describe relevant properties of a type of engine
1272     private static class EngineDescription {
1273         final String name;
1274         final boolean supportsParameter;
1275         final String constructorParameterClassName;
1276         private volatile Class<?> constructorParameterClass;
1277 
1278         EngineDescription(String name, boolean sp, String paramName) {
1279             this.name = name;
1280             this.supportsParameter = sp;
1281             this.constructorParameterClassName = paramName;
1282         }
1283         Class<?> getConstructorParameterClass() throws ClassNotFoundException {
1284             Class<?> clazz = constructorParameterClass;
1285             if (clazz == null) {
1286                 clazz = Class.forName(constructorParameterClassName);
1287                 constructorParameterClass = clazz;
1288             }
1289             return clazz;
1290         }
1291     }
1292 
1293     // built in knowledge of the engine types shipped as part of the JDK
1294     private static final Map<String,EngineDescription> knownEngines;
1295 
1296     private static void addEngine(String name, boolean sp, String paramName) {
1297         EngineDescription ed = new EngineDescription(name, sp, paramName);
1298         // also index by canonical name to avoid toLowerCase() for some lookups
1299         knownEngines.put(name.toLowerCase(ENGLISH), ed);
1300         knownEngines.put(name, ed);
1301     }
1302 
1303     static {
1304         knownEngines = new HashMap<String,EngineDescription>();
1305         // JCA
1306         addEngine("AlgorithmParameterGenerator",        false, null);
1307         addEngine("AlgorithmParameters",                false, null);
1308         addEngine("KeyFactory",                         false, null);
1309         addEngine("KeyPairGenerator",                   false, null);
1310         addEngine("KeyStore",                           false, null);
1311         addEngine("MessageDigest",                      false, null);
1312         addEngine("SecureRandom",                       false, null);
1313         addEngine("Signature",                          true,  null);
1314         addEngine("CertificateFactory",                 false, null);
1315         addEngine("CertPathBuilder",                    false, null);
1316         addEngine("CertPathValidator",                  false, null);
1317         addEngine("CertStore",                          false,
1318                             "java.security.cert.CertStoreParameters");
1319         // JCE
1320         addEngine("Cipher",                             true,  null);
1321         addEngine("ExemptionMechanism",                 false, null);
1322         addEngine("Mac",                                true,  null);
1323         addEngine("KeyAgreement",                       true,  null);
1324         addEngine("KeyGenerator",                       false, null);
1325         addEngine("SecretKeyFactory",                   false, null);
1326         // JSSE
1327         addEngine("KeyManagerFactory",                  false, null);
1328         addEngine("SSLContext",                         false, null);
1329         addEngine("TrustManagerFactory",                false, null);
1330         // JGSS
1331         addEngine("GssApiMechanism",                    false, null);
1332         // SASL
1333         addEngine("SaslClientFactory",                  false, null);
1334         addEngine("SaslServerFactory",                  false, null);
1335         // POLICY
1336         addEngine("Policy",                             false,
1337                             "java.security.Policy$Parameters");
1338         // CONFIGURATION
1339         addEngine("Configuration",                      false,
1340                             "javax.security.auth.login.Configuration$Parameters");
1341         // XML DSig
1342         addEngine("XMLSignatureFactory",                false, null);
1343         addEngine("KeyInfoFactory",                     false, null);
1344         addEngine("TransformService",                   false, null);
1345         // Smart Card I/O
1346         addEngine("TerminalFactory",                    false,
1347                             "java.lang.Object");
1348     }
1349 
1350     // get the "standard" (mixed-case) engine name for arbitary case engine name
1351     // if there is no known engine by that name, return s
1352     private static String getEngineName(String s) {
1353         // try original case first, usually correct
1354         EngineDescription e = knownEngines.get(s);
1355         if (e == null) {
1356             e = knownEngines.get(s.toLowerCase(ENGLISH));
1357         }
1358         return (e == null) ? s : e.name;
1359     }
1360 
1361     /**
1362      * The description of a security service. It encapsulates the properties
1363      * of a service and contains a factory method to obtain new implementation
1364      * instances of this service.
1365      *
1366      * <p>Each service has a provider that offers the service, a type,
1367      * an algorithm name, and the name of the class that implements the
1368      * service. Optionally, it also includes a list of alternate algorithm
1369      * names for this service (aliases) and attributes, which are a map of
1370      * (name, value) String pairs.
1371      *
1372      * <p>This class defines the methods {@link #supportsParameter
1373      * supportsParameter()} and {@link #newInstance newInstance()}
1374      * which are used by the Java security framework when it searches for
1375      * suitable services and instantes them. The valid arguments to those
1376      * methods depend on the type of service. For the service types defined
1377      * within Java SE, see the
1378      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1379      * Java Cryptography Architecture API Specification &amp; Reference </a>
1380      * for the valid values.
1381      * Note that components outside of Java SE can define additional types of
1382      * services and their behavior.
1383      *
1384      * <p>Instances of this class are immutable.
1385      *
1386      * @since 1.5
1387      */
1388     public static class Service {
1389 
1390         private String type, algorithm, className;
1391         private final Provider provider;
1392         private List<String> aliases;
1393         private Map<UString,String> attributes;
1394 
1395         // Reference to the cached implementation Class object
1396         private volatile Reference<Class<?>> classRef;
1397 
1398         // flag indicating whether this service has its attributes for
1399         // supportedKeyFormats or supportedKeyClasses set
1400         // if null, the values have not been initialized
1401         // if TRUE, at least one of supportedFormats/Classes is non null
1402         private volatile Boolean hasKeyAttributes;
1403 
1404         // supported encoding formats
1405         private String[] supportedFormats;
1406 
1407         // names of the supported key (super) classes
1408         private Class[] supportedClasses;
1409 
1410         // whether this service has been registered with the Provider
1411         private boolean registered;
1412 
1413         private static final Class<?>[] CLASS0 = new Class<?>[0];
1414 
1415         // this constructor and these methods are used for parsing
1416         // the legacy string properties.
1417 
1418         private Service(Provider provider) {
1419             this.provider = provider;
1420             aliases = Collections.<String>emptyList();
1421             attributes = Collections.<UString,String>emptyMap();
1422         }
1423 
1424         private boolean isValid() {
1425             return (type != null) && (algorithm != null) && (className != null);
1426         }
1427 
1428         private void addAlias(String alias) {
1429             if (aliases.isEmpty()) {
1430                 aliases = new ArrayList<String>(2);
1431             }
1432             aliases.add(alias);
1433         }
1434 
1435         void addAttribute(String type, String value) {
1436             if (attributes.isEmpty()) {
1437                 attributes = new HashMap<UString,String>(8);
1438             }
1439             attributes.put(new UString(type), value);
1440         }
1441 
1442         /**
1443          * Construct a new service.
1444          *
1445          * @param provider the provider that offers this service
1446          * @param type the type of this service
1447          * @param algorithm the algorithm name
1448          * @param className the name of the class implementing this service
1449          * @param aliases List of aliases or null if algorithm has no aliases
1450          * @param attributes Map of attributes or null if this implementation
1451          *                   has no attributes
1452          *
1453          * @throws NullPointerException if provider, type, algorithm, or
1454          * className is null
1455          */
1456         public Service(Provider provider, String type, String algorithm,
1457                 String className, List<String> aliases,
1458                 Map<String,String> attributes) {
1459             if ((provider == null) || (type == null) ||
1460                     (algorithm == null) || (className == null)) {
1461                 throw new NullPointerException();
1462             }
1463             this.provider = provider;
1464             this.type = getEngineName(type);
1465             this.algorithm = algorithm;
1466             this.className = className;
1467             if (aliases == null) {
1468                 this.aliases = Collections.<String>emptyList();
1469             } else {
1470                 this.aliases = new ArrayList<String>(aliases);
1471             }
1472             if (attributes == null) {
1473                 this.attributes = Collections.<UString,String>emptyMap();
1474             } else {
1475                 this.attributes = new HashMap<UString,String>();
1476                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
1477                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
1478                 }
1479             }
1480         }
1481 
1482         /**
1483          * Get the type of this service. For example, {@code MessageDigest}.
1484          *
1485          * @return the type of this service
1486          */
1487         public final String getType() {
1488             return type;
1489         }
1490 
1491         /**
1492          * Return the name of the algorithm of this service. For example,
1493          * {@code SHA-1}.
1494          *
1495          * @return the algorithm of this service
1496          */
1497         public final String getAlgorithm() {
1498             return algorithm;
1499         }
1500 
1501         /**
1502          * Return the Provider of this service.
1503          *
1504          * @return the Provider of this service
1505          */
1506         public final Provider getProvider() {
1507             return provider;
1508         }
1509 
1510         /**
1511          * Return the name of the class implementing this service.
1512          *
1513          * @return the name of the class implementing this service
1514          */
1515         public final String getClassName() {
1516             return className;
1517         }
1518 
1519         // internal only
1520         private final List<String> getAliases() {
1521             return aliases;
1522         }
1523 
1524         /**
1525          * Return the value of the specified attribute or null if this
1526          * attribute is not set for this Service.
1527          *
1528          * @param name the name of the requested attribute
1529          *
1530          * @return the value of the specified attribute or null if the
1531          *         attribute is not present
1532          *
1533          * @throws NullPointerException if name is null
1534          */
1535         public final String getAttribute(String name) {
1536             if (name == null) {
1537                 throw new NullPointerException();
1538             }
1539             return attributes.get(new UString(name));
1540         }
1541 
1542         /**
1543          * Return a new instance of the implementation described by this
1544          * service. The security provider framework uses this method to
1545          * construct implementations. Applications will typically not need
1546          * to call it.
1547          *
1548          * <p>The default implementation uses reflection to invoke the
1549          * standard constructor for this type of service.
1550          * Security providers can override this method to implement
1551          * instantiation in a different way.
1552          * For details and the values of constructorParameter that are
1553          * valid for the various types of services see the
1554          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1555          * Java Cryptography Architecture API Specification &amp;
1556          * Reference</a>.
1557          *
1558          * @param constructorParameter the value to pass to the constructor,
1559          * or null if this type of service does not use a constructorParameter.
1560          *
1561          * @return a new implementation of this service
1562          *
1563          * @throws InvalidParameterException if the value of
1564          * constructorParameter is invalid for this type of service.
1565          * @throws NoSuchAlgorithmException if instantation failed for
1566          * any other reason.
1567          */
1568         public Object newInstance(Object constructorParameter)
1569                 throws NoSuchAlgorithmException {
1570             if (registered == false) {
1571                 if (provider.getService(type, algorithm) != this) {
1572                     throw new NoSuchAlgorithmException
1573                         ("Service not registered with Provider "
1574                         + provider.getName() + ": " + this);
1575                 }
1576                 registered = true;
1577             }
1578             try {
1579                 EngineDescription cap = knownEngines.get(type);
1580                 if (cap == null) {
1581                     // unknown engine type, use generic code
1582                     // this is the code path future for non-core
1583                     // optional packages
1584                     return newInstanceGeneric(constructorParameter);
1585                 }
1586                 if (cap.constructorParameterClassName == null) {
1587                     if (constructorParameter != null) {
1588                         throw new InvalidParameterException
1589                             ("constructorParameter not used with " + type
1590                             + " engines");
1591                     }
1592                     Class<?> clazz = getImplClass();
1593                     return clazz.newInstance();
1594                 } else {
1595                     Class<?> paramClass = cap.getConstructorParameterClass();
1596                     if (constructorParameter != null) {
1597                         Class<?> argClass = constructorParameter.getClass();
1598                         if (paramClass.isAssignableFrom(argClass) == false) {
1599                             throw new InvalidParameterException
1600                             ("constructorParameter must be instanceof "
1601                             + cap.constructorParameterClassName.replace('$', '.')
1602                             + " for engine type " + type);
1603                         }
1604                     }
1605                     Class<?> clazz = getImplClass();
1606                     Constructor<?> cons = clazz.getConstructor(paramClass);
1607                     return cons.newInstance(constructorParameter);
1608                 }
1609             } catch (NoSuchAlgorithmException e) {
1610                 throw e;
1611             } catch (InvocationTargetException e) {
1612                 throw new NoSuchAlgorithmException
1613                     ("Error constructing implementation (algorithm: "
1614                     + algorithm + ", provider: " + provider.getName()
1615                     + ", class: " + className + ")", e.getCause());
1616             } catch (Exception e) {
1617                 throw new NoSuchAlgorithmException
1618                     ("Error constructing implementation (algorithm: "
1619                     + algorithm + ", provider: " + provider.getName()
1620                     + ", class: " + className + ")", e);
1621             }
1622         }
1623 
1624         // return the implementation Class object for this service
1625         private Class<?> getImplClass() throws NoSuchAlgorithmException {
1626             try {
1627                 Reference<Class<?>> ref = classRef;
1628                 Class<?> clazz = (ref == null) ? null : ref.get();
1629                 if (clazz == null) {
1630                     ClassLoader cl = provider.getClass().getClassLoader();
1631                     if (cl == null) {
1632                         clazz = Class.forName(className);
1633                     } else {
1634                         clazz = cl.loadClass(className);
1635                     }
1636                     classRef = new WeakReference<Class<?>>(clazz);
1637                 }
1638                 return clazz;
1639             } catch (ClassNotFoundException e) {
1640                 throw new NoSuchAlgorithmException
1641                     ("class configured for " + type + "(provider: " +
1642                     provider.getName() + ")" + "cannot be found.", e);
1643             }
1644         }
1645 
1646         /**
1647          * Generic code path for unknown engine types. Call the
1648          * no-args constructor if constructorParameter is null, otherwise
1649          * use the first matching constructor.
1650          */
1651         private Object newInstanceGeneric(Object constructorParameter)
1652                 throws Exception {
1653             Class<?> clazz = getImplClass();
1654             if (constructorParameter == null) {
1655                 Object o = clazz.newInstance();
1656                 return o;
1657             }
1658             Class<?> argClass = constructorParameter.getClass();
1659             Constructor[] cons = clazz.getConstructors();
1660             // find first public constructor that can take the
1661             // argument as parameter
1662             for (int i = 0; i < cons.length; i++) {
1663                 Constructor<?> con = cons[i];
1664                 Class<?>[] paramTypes = con.getParameterTypes();
1665                 if (paramTypes.length != 1) {
1666                     continue;
1667                 }
1668                 if (paramTypes[0].isAssignableFrom(argClass) == false) {
1669                     continue;
1670                 }
1671                 Object o = con.newInstance(new Object[] {constructorParameter});
1672                 return o;
1673             }
1674             throw new NoSuchAlgorithmException("No constructor matching "
1675                 + argClass.getName() + " found in class " + className);
1676         }
1677 
1678         /**
1679          * Test whether this Service can use the specified parameter.
1680          * Returns false if this service cannot use the parameter. Returns
1681          * true if this service can use the parameter, if a fast test is
1682          * infeasible, or if the status is unknown.
1683          *
1684          * <p>The security provider framework uses this method with
1685          * some types of services to quickly exclude non-matching
1686          * implementations for consideration.
1687          * Applications will typically not need to call it.
1688          *
1689          * <p>For details and the values of parameter that are valid for the
1690          * various types of services see the top of this class and the
1691          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1692          * Java Cryptography Architecture API Specification &amp;
1693          * Reference</a>.
1694          * Security providers can override it to implement their own test.
1695          *
1696          * @param parameter the parameter to test
1697          *
1698          * @return false if this this service cannot use the specified
1699          * parameter; true if it can possibly use the parameter
1700          *
1701          * @throws InvalidParameterException if the value of parameter is
1702          * invalid for this type of service or if this method cannot be
1703          * used with this type of service
1704          */
1705         public boolean supportsParameter(Object parameter) {
1706             EngineDescription cap = knownEngines.get(type);
1707             if (cap == null) {
1708                 // unknown engine type, return true by default
1709                 return true;
1710             }
1711             if (cap.supportsParameter == false) {
1712                 throw new InvalidParameterException("supportsParameter() not "
1713                     + "used with " + type + " engines");
1714             }
1715             // allow null for keys without attributes for compatibility
1716             if ((parameter != null) && (parameter instanceof Key == false)) {
1717                 throw new InvalidParameterException
1718                     ("Parameter must be instanceof Key for engine " + type);
1719             }
1720             if (hasKeyAttributes() == false) {
1721                 return true;
1722             }
1723             if (parameter == null) {
1724                 return false;
1725             }
1726             Key key = (Key)parameter;
1727             if (supportsKeyFormat(key)) {
1728                 return true;
1729             }
1730             if (supportsKeyClass(key)) {
1731                 return true;
1732             }
1733             return false;
1734         }
1735 
1736         /**
1737          * Return whether this service has its Supported* properties for
1738          * keys defined. Parses the attributes if not yet initialized.
1739          */
1740         private boolean hasKeyAttributes() {
1741             Boolean b = hasKeyAttributes;
1742             if (b == null) {
1743                 synchronized (this) {
1744                     String s;
1745                     s = getAttribute("SupportedKeyFormats");
1746                     if (s != null) {
1747                         supportedFormats = s.split("\\|");
1748                     }
1749                     s = getAttribute("SupportedKeyClasses");
1750                     if (s != null) {
1751                         String[] classNames = s.split("\\|");
1752                         List<Class<?>> classList =
1753                             new ArrayList<>(classNames.length);
1754                         for (String className : classNames) {
1755                             Class<?> clazz = getKeyClass(className);
1756                             if (clazz != null) {
1757                                 classList.add(clazz);
1758                             }
1759                         }
1760                         supportedClasses = classList.toArray(CLASS0);
1761                     }
1762                     boolean bool = (supportedFormats != null)
1763                         || (supportedClasses != null);
1764                     b = Boolean.valueOf(bool);
1765                     hasKeyAttributes = b;
1766                 }
1767             }
1768             return b.booleanValue();
1769         }
1770 
1771         // get the key class object of the specified name
1772         private Class<?> getKeyClass(String name) {
1773             try {
1774                 return Class.forName(name);
1775             } catch (ClassNotFoundException e) {
1776                 // ignore
1777             }
1778             try {
1779                 ClassLoader cl = provider.getClass().getClassLoader();
1780                 if (cl != null) {
1781                     return cl.loadClass(name);
1782                 }
1783             } catch (ClassNotFoundException e) {
1784                 // ignore
1785             }
1786             return null;
1787         }
1788 
1789         private boolean supportsKeyFormat(Key key) {
1790             if (supportedFormats == null) {
1791                 return false;
1792             }
1793             String format = key.getFormat();
1794             if (format == null) {
1795                 return false;
1796             }
1797             for (String supportedFormat : supportedFormats) {
1798                 if (supportedFormat.equals(format)) {
1799                     return true;
1800                 }
1801             }
1802             return false;
1803         }
1804 
1805         private boolean supportsKeyClass(Key key) {
1806             if (supportedClasses == null) {
1807                 return false;
1808             }
1809             Class<?> keyClass = key.getClass();
1810             for (Class<?> clazz : supportedClasses) {
1811                 if (clazz.isAssignableFrom(keyClass)) {
1812                     return true;
1813                 }
1814             }
1815             return false;
1816         }
1817 
1818         /**
1819          * Return a String representation of this service.
1820          *
1821          * @return a String representation of this service.
1822          */
1823         public String toString() {
1824             String aString = aliases.isEmpty()
1825                 ? "" : "\r\n  aliases: " + aliases.toString();
1826             String attrs = attributes.isEmpty()
1827                 ? "" : "\r\n  attributes: " + attributes.toString();
1828             return provider.getName() + ": " + type + "." + algorithm
1829                 + " -> " + className + aString + attrs + "\r\n";
1830         }
1831 
1832     }
1833 
1834 }