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