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