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