1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.security; 19 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.NotActiveException; 23import java.util.ArrayList; 24import java.util.Collection; 25import java.util.Collections; 26import java.util.Enumeration; 27import java.util.HashMap; 28import java.util.HashSet; 29import java.util.Iterator; 30import java.util.LinkedHashMap; 31import java.util.LinkedHashSet; 32import java.util.List; 33import java.util.Locale; 34import java.util.Map; 35import java.util.Properties; 36import java.util.Set; 37import org.apache.harmony.security.fortress.Services; 38 39/** 40 * {@code Provider} is the abstract superclass for all security providers in the 41 * Java security infrastructure. 42 */ 43public abstract class Provider extends Properties { 44 private static final long serialVersionUID = -4298000515446427739L; 45 46 private String name; 47 48 private double version; 49 50 // String representation of the provider version number. 51 private transient String versionString; 52 53 private String info; 54 55 //The provider preference order number. 56 // Equals -1 for non registered provider. 57 private transient int providerNumber = -1; 58 59 // Contains "Service.Algorithm" and Provider.Service classes added using 60 // putService() 61 private transient LinkedHashMap<String, Service> serviceTable; 62 63 // Contains "Service.Alias" and Provider.Service classes added using 64 // putService() 65 private transient LinkedHashMap<String, Service> aliasTable; 66 67 // Contains "Service.Algorithm" and Provider.Service classes added using 68 // put() 69 private transient LinkedHashMap<String, Service> propertyServiceTable; 70 71 // Contains "Service.Alias" and Provider.Service classes added using put() 72 private transient LinkedHashMap<String, Service> propertyAliasTable; 73 74 // The properties changed via put() 75 private transient Properties changedProperties; 76 77 // For getService(String type, String algorithm) optimization: 78 // previous result 79 private transient Provider.Service returnedService; 80 // previous parameters 81 private transient String lastAlgorithm; 82 // last name 83 private transient String lastServiceName; 84 85 // For getServices() optimization: 86 private transient Set<Service> lastServicesSet; 87 88 // For getService(String type) optimization: 89 private transient String lastType; 90 // last Service found by type 91 private transient Provider.Service lastServicesByType; 92 93 /** 94 * Constructs a new instance of {@code Provider} with its name, version and 95 * description. 96 * 97 * @param name 98 * the name of the provider. 99 * @param version 100 * the version of the provider. 101 * @param info 102 * a description of the provider. 103 */ 104 protected Provider(String name, double version, String info) { 105 this.name = name; 106 this.version = version; 107 this.info = info; 108 versionString = String.valueOf(version); 109 putProviderInfo(); 110 } 111 112 /** 113 * Returns the name of this provider. 114 * 115 * @return the name of this provider. 116 */ 117 public String getName() { 118 return name; 119 } 120 121 /** 122 * Returns the version number for the services being provided. 123 * 124 * @return the version number for the services being provided. 125 */ 126 public double getVersion() { 127 return version; 128 } 129 130 /** 131 * Returns a description of the services being provided. 132 * 133 * @return a description of the services being provided. 134 */ 135 public String getInfo() { 136 return info; 137 } 138 139 /** 140 * Returns a string containing a concise, human-readable description of 141 * this {@code Provider} including its name and its version. 142 * 143 * @return a printable representation for this {@code Provider}. 144 */ 145 @Override 146 public String toString() { 147 return name + " version " + version; 148 } 149 150 /** 151 * Clears all properties used to look up services implemented by this 152 * {@code Provider}. 153 */ 154 @Override 155 public synchronized void clear() { 156 super.clear(); 157 if (serviceTable != null) { 158 serviceTable.clear(); 159 } 160 if (propertyServiceTable != null) { 161 propertyServiceTable.clear(); 162 } 163 if (aliasTable != null) { 164 aliasTable.clear(); 165 } 166 if (propertyAliasTable != null) { 167 propertyAliasTable.clear(); 168 } 169 changedProperties = null; 170 putProviderInfo(); 171 if (providerNumber != -1) { 172 // if registered then refresh Services 173 Services.setNeedRefresh(); 174 } 175 servicesChanged(); 176 } 177 178 @Override 179 public synchronized void load(InputStream inStream) throws IOException { 180 Properties tmp = new Properties(); 181 tmp.load(inStream); 182 myPutAll(tmp); 183 } 184 185 /** 186 * Copies all from the provided map to this {@code Provider}. 187 * @param t 188 * the mappings to copy to this provider. 189 */ 190 @Override 191 public synchronized void putAll(Map<?,?> t) { 192 myPutAll(t); 193 } 194 195 private void myPutAll(Map<?,?> t) { 196 if (changedProperties == null) { 197 changedProperties = new Properties(); 198 } 199 Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator(); 200 Object key; 201 Object value; 202 while (it.hasNext()) { 203 Map.Entry<?, ?> entry = it.next(); 204 key = entry.getKey(); 205 if (key instanceof String && ((String) key).startsWith("Provider.")) { 206 // Provider service type is reserved 207 continue; 208 } 209 value = entry.getValue(); 210 super.put(key, value); 211 if (changedProperties.remove(key) == null) { 212 removeFromPropertyServiceTable(key); 213 } 214 changedProperties.put(key, value); 215 } 216 if (providerNumber != -1) { 217 // if registered then refresh Services 218 Services.setNeedRefresh(); 219 } 220 } 221 222 @Override 223 public synchronized Set<Map.Entry<Object,Object>> entrySet() { 224 return Collections.unmodifiableSet(super.entrySet()); 225 } 226 227 @Override 228 public Set<Object> keySet() { 229 return Collections.unmodifiableSet(super.keySet()); 230 } 231 232 @Override 233 public Collection<Object> values() { 234 return Collections.unmodifiableCollection(super.values()); 235 } 236 237 /** 238 * Maps the specified {@code key} property name to the specified {@code 239 * value}. 240 * 241 * @param key 242 * the name of the property. 243 * @param value 244 * the value of the property. 245 * @return the value that was previously mapped to the specified {@code key} 246 * ,or {@code null} if it did not have one. 247 */ 248 @Override 249 public synchronized Object put(Object key, Object value) { 250 if (key instanceof String && ((String) key).startsWith("Provider.")) { 251 // Provider service type is reserved 252 return null; 253 } 254 if (providerNumber != -1) { 255 // if registered then refresh Services 256 Services.setNeedRefresh(); 257 } 258 if (changedProperties != null && changedProperties.remove(key) == null) { 259 removeFromPropertyServiceTable(key); 260 } 261 if (changedProperties == null) { 262 changedProperties = new Properties(); 263 } 264 changedProperties.put(key, value); 265 return super.put(key, value); 266 } 267 268 /** 269 * Removes the specified {@code key} and its associated value from this 270 * {@code Provider}. 271 * 272 * @param key 273 * the name of the property 274 * @return the value that was mapped to the specified {@code key} ,or 275 * {@code null} if no mapping was present 276 */ 277 @Override 278 public synchronized Object remove(Object key) { 279 if (key instanceof String && ((String) key).startsWith("Provider.")) { 280 // Provider service type is reserved 281 return null; 282 } 283 if (providerNumber != -1) { 284 // if registered then refresh Services 285 Services.setNeedRefresh(); 286 } 287 if (changedProperties != null && changedProperties.remove(key) == null) { 288 removeFromPropertyServiceTable(key); 289 if (changedProperties.size() == 0) { 290 changedProperties = null; 291 } 292 } 293 return super.remove(key); 294 } 295 296 /** 297 * Returns true if this provider implements the given algorithm. Caller 298 * must specify the cryptographic service and specify constraints via the 299 * attribute name and value. 300 * 301 * @param serv 302 * Crypto service. 303 * @param alg 304 * Algorithm or type. 305 * @param attribute 306 * The attribute name or {@code null}. 307 * @param val 308 * The attribute value. 309 * @return 310 */ 311 boolean implementsAlg(String serv, String alg, String attribute, String val) { 312 String servAlg = serv + "." + alg; 313 String prop = getPropertyIgnoreCase(servAlg); 314 if (prop == null) { 315 alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); 316 if (alg != null) { 317 servAlg = serv + "." + alg; 318 prop = getPropertyIgnoreCase(servAlg); 319 } 320 } 321 if (prop != null) { 322 if (attribute == null) { 323 return true; 324 } 325 return checkAttribute(servAlg, attribute, val); 326 } 327 return false; 328 } 329 330 /** 331 * Returns true if this provider has the same value as is given for the 332 * given attribute 333 */ 334 private boolean checkAttribute(String servAlg, String attribute, String val) { 335 336 String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute); 337 if (attributeValue != null) { 338 if (attribute.equalsIgnoreCase("KeySize")) { 339 if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) { 340 return true; 341 } 342 } else { // other attributes 343 if (attributeValue.equalsIgnoreCase(val)) { 344 return true; 345 } 346 } 347 } 348 return false; 349 } 350 351 /** 352 * 353 * Set the provider preference order number. 354 * 355 * @param n 356 */ 357 void setProviderNumber(int n) { 358 providerNumber = n; 359 } 360 361 /** 362 * 363 * Get the provider preference order number. 364 * 365 * @return 366 */ 367 int getProviderNumber() { 368 return providerNumber; 369 } 370 371 /** 372 * Get the service of the specified type 373 * 374 */ 375 synchronized Provider.Service getService(String type) { 376 updatePropertyServiceTable(); 377 if (lastServicesByType != null && type.equals(lastType)) { 378 return lastServicesByType; 379 } 380 Provider.Service service; 381 for (Iterator<Service> it = getServices().iterator(); it.hasNext();) { 382 service = it.next(); 383 if (type.equals(service.type)) { 384 lastType = type; 385 lastServicesByType = service; 386 return service; 387 } 388 } 389 return null; 390 } 391 392 /** 393 * Returns the service with the specified {@code type} implementing the 394 * specified {@code algorithm}, or {@code null} if no such implementation 395 * exists. 396 * <p> 397 * If two services match the requested type and algorithm, the one added 398 * with the {@link #putService(Service)} is returned (as opposed to the one 399 * added via {@link #put(Object, Object)}. 400 * 401 * @param type 402 * the type of the service (for example {@code KeyPairGenerator}) 403 * @param algorithm 404 * the algorithm name (case insensitive) 405 * @return the requested service, or {@code null} if no such implementation 406 * exists 407 */ 408 public synchronized Provider.Service getService(String type, 409 String algorithm) { 410 if (type == null) { 411 throw new NullPointerException("type == null"); 412 } else if (algorithm == null) { 413 throw new NullPointerException("algorithm == null"); 414 } 415 416 if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) { 417 return returnedService; 418 } 419 420 String key = key(type, algorithm); 421 Object o = null; 422 if (serviceTable != null) { 423 o = serviceTable.get(key); 424 } 425 if (o == null && aliasTable != null) { 426 o = aliasTable.get(key); 427 } 428 if (o == null) { 429 updatePropertyServiceTable(); 430 } 431 if (o == null && propertyServiceTable != null) { 432 o = propertyServiceTable.get(key); 433 } 434 if (o == null && propertyAliasTable != null) { 435 o = propertyAliasTable.get(key); 436 } 437 438 if (o != null) { 439 lastServiceName = type; 440 lastAlgorithm = algorithm; 441 returnedService = (Provider.Service) o; 442 return returnedService; 443 } 444 return null; 445 } 446 447 /** 448 * Returns an unmodifiable {@code Set} of all services registered by this 449 * provider. 450 * 451 * @return an unmodifiable {@code Set} of all services registered by this 452 * provider 453 */ 454 public synchronized Set<Provider.Service> getServices() { 455 updatePropertyServiceTable(); 456 if (lastServicesSet != null) { 457 return lastServicesSet; 458 } 459 if (serviceTable != null) { 460 lastServicesSet = new LinkedHashSet<Service>(serviceTable.values()); 461 } else { 462 lastServicesSet = new LinkedHashSet<Service>(); 463 } 464 if (propertyServiceTable != null) { 465 lastServicesSet.addAll(propertyServiceTable.values()); 466 } 467 lastServicesSet = Collections.unmodifiableSet(lastServicesSet); 468 return lastServicesSet; 469 } 470 471 /** 472 * Adds a {@code Service} to this {@code Provider}. If a service with the 473 * same name was registered via this method, it is replace. 474 * 475 * @param s 476 * the {@code Service} to register 477 */ 478 protected synchronized void putService(Provider.Service s) { 479 if (s == null) { 480 throw new NullPointerException("s == null"); 481 } 482 if ("Provider".equals(s.getType())) { // Provider service type cannot be added 483 return; 484 } 485 servicesChanged(); 486 if (serviceTable == null) { 487 serviceTable = new LinkedHashMap<String, Service>(128); 488 } 489 serviceTable.put(key(s.type, s.algorithm), s); 490 if (s.aliases != null) { 491 if (aliasTable == null) { 492 aliasTable = new LinkedHashMap<String, Service>(256); 493 } 494 for (String alias : s.getAliases()) { 495 aliasTable.put(key(s.type, alias), s); 496 } 497 } 498 serviceInfoToProperties(s); 499 } 500 501 /** 502 * Removes a previously registered {@code Service} from this {@code 503 * Provider}. 504 * 505 * @param s 506 * the {@code Service} to remove 507 * @throws NullPointerException 508 * if {@code s} is {@code null} 509 */ 510 protected synchronized void removeService(Provider.Service s) { 511 if (s == null) { 512 throw new NullPointerException("s == null"); 513 } 514 servicesChanged(); 515 if (serviceTable != null) { 516 serviceTable.remove(key(s.type, s.algorithm)); 517 } 518 if (aliasTable != null && s.aliases != null) { 519 for (String alias: s.getAliases()) { 520 aliasTable.remove(key(s.type, alias)); 521 } 522 } 523 serviceInfoFromProperties(s); 524 } 525 526 /** 527 * Add Service information to the provider's properties. 528 */ 529 private void serviceInfoToProperties(Provider.Service s) { 530 super.put(s.type + "." + s.algorithm, s.className); 531 if (s.aliases != null) { 532 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 533 super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); 534 } 535 } 536 if (s.attributes != null) { 537 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 538 super.put(s.type + "." + s.algorithm + " " + entry.getKey(), 539 entry.getValue()); 540 } 541 } 542 if (providerNumber != -1) { 543 // if registered then refresh Services 544 Services.setNeedRefresh(); 545 } 546 } 547 548 /** 549 * Remove Service information from the provider's properties. 550 */ 551 private void serviceInfoFromProperties(Provider.Service s) { 552 super.remove(s.type + "." + s.algorithm); 553 if (s.aliases != null) { 554 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 555 super.remove("Alg.Alias." + s.type + "." + i.next()); 556 } 557 } 558 if (s.attributes != null) { 559 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 560 super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); 561 } 562 } 563 if (providerNumber != -1) { 564 // if registered then refresh Services 565 Services.setNeedRefresh(); 566 } 567 } 568 569 // Remove property information from provider Services 570 private void removeFromPropertyServiceTable(Object key) { 571 if (key == null || !(key instanceof String)) { 572 return; 573 } 574 String k = (String) key; 575 if (k.startsWith("Provider.")) { // Provider service type is reserved 576 return; 577 } 578 Provider.Service s; 579 String serviceName; 580 String algorithm = null; 581 String attribute = null; 582 int i; 583 if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 584 String aliasName; 585 String service_alias = k.substring(10); 586 i = service_alias.indexOf('.'); 587 serviceName = service_alias.substring(0, i); 588 aliasName = service_alias.substring(i + 1); 589 if (propertyAliasTable != null) { 590 propertyAliasTable.remove(key(serviceName, aliasName)); 591 } 592 if (propertyServiceTable != null) { 593 for (Iterator<Service> it = propertyServiceTable.values().iterator(); it 594 .hasNext();) { 595 s = it.next(); 596 if (s.aliases.contains(aliasName)) { 597 s.aliases.remove(aliasName); 598 return; 599 } 600 } 601 } 602 return; 603 } 604 int j = k.indexOf('.'); 605 if (j == -1) { // unknown format 606 return; 607 } 608 609 i = k.indexOf(' '); 610 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 611 serviceName = k.substring(0, j); 612 algorithm = k.substring(j + 1); 613 if (propertyServiceTable != null) { 614 Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm)); 615 if (ser != null && propertyAliasTable != null 616 && ser.aliases != null) { 617 for (String alias : ser.aliases) { 618 propertyAliasTable.remove(key(serviceName, alias)); 619 } 620 } 621 } 622 } else { 623 // <crypto_service>.<algorithm_or_type> 624 // <attribute_name>=<attrValue> 625 attribute = k.substring(i + 1); 626 serviceName = k.substring(0, j); 627 algorithm = k.substring(j + 1, i); 628 if (propertyServiceTable != null) { 629 Object o = propertyServiceTable.get(key(serviceName, algorithm)); 630 if (o != null) { 631 s = (Provider.Service) o; 632 s.attributes.remove(attribute); 633 } 634 } 635 } 636 } 637 638 // Update provider Services if the properties was changed 639 private void updatePropertyServiceTable() { 640 Object _key; 641 Object _value; 642 Provider.Service s; 643 String serviceName; 644 String algorithm; 645 if (changedProperties == null || changedProperties.isEmpty()) { 646 return; 647 } 648 for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it 649 .hasNext();) { 650 Map.Entry<Object, Object> entry = it.next(); 651 _key = entry.getKey(); 652 _value = entry.getValue(); 653 if (_key == null || _value == null || !(_key instanceof String) 654 || !(_value instanceof String)) { 655 continue; 656 } 657 String key = (String) _key; 658 String value = (String) _value; 659 if (key.startsWith("Provider")) { 660 // Provider service type is reserved 661 continue; 662 } 663 int i; 664 if (key.startsWith("Alg.Alias.")) { 665 // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 666 String aliasName; 667 String service_alias = key.substring(10); 668 i = service_alias.indexOf('.'); 669 serviceName = service_alias.substring(0, i); 670 aliasName = service_alias.substring(i + 1); 671 algorithm = value; 672 String propertyServiceTableKey = key(serviceName, algorithm); 673 Object o = null; 674 if (propertyServiceTable == null) { 675 propertyServiceTable = new LinkedHashMap<String, Service>(128); 676 } else { 677 o = propertyServiceTable.get(propertyServiceTableKey); 678 } 679 if (o != null) { 680 s = (Provider.Service) o; 681 s.addAlias(aliasName); 682 if (propertyAliasTable == null) { 683 propertyAliasTable = new LinkedHashMap<String, Service>(256); 684 } 685 propertyAliasTable.put(key(serviceName, aliasName), s); 686 } else { 687 String className = (String) changedProperties 688 .get(serviceName + "." + algorithm); 689 if (className != null) { 690 List<String> l = new ArrayList<String>(); 691 l.add(aliasName); 692 s = new Provider.Service(this, serviceName, algorithm, 693 className, l, new HashMap<String, String>()); 694 propertyServiceTable.put(propertyServiceTableKey, s); 695 if (propertyAliasTable == null) { 696 propertyAliasTable = new LinkedHashMap<String, Service>(256); 697 } 698 propertyAliasTable.put(key(serviceName, aliasName), s); 699 } 700 } 701 continue; 702 } 703 int j = key.indexOf('.'); 704 if (j == -1) { // unknown format 705 continue; 706 } 707 i = key.indexOf(' '); 708 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 709 serviceName = key.substring(0, j); 710 algorithm = key.substring(j + 1); 711 String propertyServiceTableKey = key(serviceName, algorithm); 712 Object o = null; 713 if (propertyServiceTable != null) { 714 o = propertyServiceTable.get(propertyServiceTableKey); 715 } 716 if (o != null) { 717 s = (Provider.Service) o; 718 s.className = value; 719 } else { 720 s = new Provider.Service(this, serviceName, algorithm, 721 value, Collections.<String>emptyList(), 722 Collections.<String,String>emptyMap()); 723 if (propertyServiceTable == null) { 724 propertyServiceTable = new LinkedHashMap<String, Service>(128); 725 } 726 propertyServiceTable.put(propertyServiceTableKey, s); 727 728 } 729 } else { 730 // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue> 731 serviceName = key.substring(0, j); 732 algorithm = key.substring(j + 1, i); 733 String attribute = key.substring(i + 1); 734 String propertyServiceTableKey = key(serviceName, algorithm); 735 Object o = null; 736 if (propertyServiceTable != null) { 737 o = propertyServiceTable.get(propertyServiceTableKey); 738 } 739 if (o != null) { 740 s = (Provider.Service) o; 741 s.putAttribute(attribute, value); 742 } else { 743 String className = (String) changedProperties 744 .get(serviceName + "." + algorithm); 745 if (className != null) { 746 Map<String, String> m = new HashMap<String, String>(); 747 m.put(attribute, value); 748 s = new Provider.Service(this, serviceName, algorithm, 749 className, new ArrayList<String>(), m); 750 if (propertyServiceTable == null) { 751 propertyServiceTable = new LinkedHashMap<String, Service>(128); 752 } 753 propertyServiceTable.put(propertyServiceTableKey, s); 754 } 755 } 756 } 757 } 758 servicesChanged(); 759 changedProperties = null; 760 } 761 762 private void servicesChanged() { 763 lastServicesByType = null; 764 lastServiceName = null; 765 lastServicesSet = null; 766 } 767 768 /** 769 * These attributes should be placed in each Provider object: 770 * Provider.id name, Provider.id version, Provider.id info, 771 * Provider.id className 772 */ 773 private void putProviderInfo() { 774 super.put("Provider.id name", (name != null) ? name : "null"); 775 super.put("Provider.id version", versionString); 776 super.put("Provider.id info", (info != null) ? info : "null"); 777 super.put("Provider.id className", this.getClass().getName()); 778 } 779 780 /** 781 * Returns the property with the specified key in the provider properties. 782 * The name is not case-sensitive. 783 */ 784 private String getPropertyIgnoreCase(String key) { 785 String res = getProperty(key); 786 if (res != null) { 787 return res; 788 } 789 for (Enumeration<?> e = propertyNames(); e.hasMoreElements(); ) { 790 String propertyName = (String) e.nextElement(); 791 if (key.equalsIgnoreCase(propertyName)) { 792 return getProperty(propertyName); 793 } 794 } 795 return null; 796 } 797 798 private static String key(String type, String algorithm) { 799 return type + '.' + algorithm.toUpperCase(Locale.US); 800 } 801 802 /** 803 * {@code Service} represents a service in the Java Security infrastructure. 804 * Each service describes its type, the algorithm it implements, to which 805 * provider it belongs and other properties. 806 */ 807 public static class Service { 808 // The provider 809 private Provider provider; 810 811 // The type of this service 812 private String type; 813 814 // The algorithm name 815 private String algorithm; 816 817 // The class implementing this service 818 private String className; 819 820 // The aliases 821 private List<String> aliases; 822 823 // The attributes 824 private Map<String,String> attributes; 825 826 // Service implementation 827 private Class<?> implementation; 828 829 // For newInstance() optimization 830 private String lastClassName; 831 832 /** 833 * Constructs a new instance of {@code Service} with the given 834 * attributes. 835 * 836 * @param provider 837 * the provider to which this service belongs. 838 * @param type 839 * the type of this service (for example {@code 840 * KeyPairGenerator}). 841 * @param algorithm 842 * the algorithm this service implements. 843 * @param className 844 * the name of the class implementing this service. 845 * @param aliases 846 * {@code List} of aliases for the algorithm name, or {@code 847 * null} if the implemented algorithm has no aliases. 848 * @param attributes 849 * {@code Map} of additional attributes, or {@code null} if 850 * this {@code Service} has no attributed. 851 * @throws NullPointerException 852 * if {@code provider, type, algorithm} or {@code className} 853 * is {@code null}. 854 */ 855 public Service(Provider provider, String type, String algorithm, 856 String className, List<String> aliases, Map<String, String> attributes) { 857 if (provider == null) { 858 throw new NullPointerException("provider == null"); 859 } else if (type == null) { 860 throw new NullPointerException("type == null"); 861 } else if (algorithm == null) { 862 throw new NullPointerException("algorithm == null"); 863 } else if (className == null) { 864 throw new NullPointerException("className == null"); 865 } 866 this.provider = provider; 867 this.type = type; 868 this.algorithm = algorithm; 869 this.className = className; 870 this.aliases = ((aliases != null) && (aliases.size() == 0)) 871 ? Collections.<String>emptyList() : aliases; 872 this.attributes = 873 ((attributes != null) && (attributes.size() == 0)) 874 ? Collections.<String,String>emptyMap() : attributes; 875 } 876 877 /** 878 * Adds an alias. 879 * 880 * @param alias the alias to add 881 */ 882 /*package*/ void addAlias(String alias) { 883 if ((aliases == null) || (aliases.size() == 0)) { 884 aliases = new ArrayList<String>(); 885 } 886 aliases.add(alias); 887 } 888 889 /** 890 * Puts a new attribute mapping. 891 * 892 * @param name the attribute name. 893 * @param value the attribute value. 894 */ 895 /*package*/ void putAttribute(String name, String value) { 896 if ((attributes == null) || (attributes.size() == 0)) { 897 attributes = new HashMap<String,String>(); 898 } 899 attributes.put(name, value); 900 } 901 902 /** 903 * Returns the type of this {@code Service}. For example {@code 904 * KeyPairGenerator}. 905 * 906 * @return the type of this {@code Service}. 907 */ 908 public final String getType() { 909 return type; 910 } 911 912 /** 913 * Returns the name of the algorithm implemented by this {@code 914 * Service}. 915 * 916 * @return the name of the algorithm implemented by this {@code 917 * Service}. 918 */ 919 public final String getAlgorithm() { 920 return algorithm; 921 } 922 923 /** 924 * Returns the {@code Provider} this {@code Service} belongs to. 925 * 926 * @return the {@code Provider} this {@code Service} belongs to. 927 */ 928 public final Provider getProvider() { 929 return provider; 930 } 931 932 /** 933 * Returns the name of the class implementing this {@code Service}. 934 * 935 * @return the name of the class implementing this {@code Service}. 936 */ 937 public final String getClassName() { 938 return className; 939 } 940 941 /** 942 * Returns the value of the attribute with the specified {@code name}. 943 * 944 * @param name 945 * the name of the attribute. 946 * @return the value of the attribute, or {@code null} if no attribute 947 * with the given name is set. 948 * @throws NullPointerException 949 * if {@code name} is {@code null}. 950 */ 951 public final String getAttribute(String name) { 952 if (name == null) { 953 throw new NullPointerException("name == null"); 954 } 955 if (attributes == null) { 956 return null; 957 } 958 return attributes.get(name); 959 } 960 961 List<String> getAliases() { 962 if (aliases == null){ 963 aliases = new ArrayList<String>(0); 964 } 965 return aliases; 966 } 967 968 /** 969 * Creates and returns a new instance of the implementation described by 970 * this {@code Service}. 971 * 972 * @param constructorParameter 973 * the parameter that is used by the constructor, or {@code 974 * null} if the implementation does not declare a constructor 975 * parameter. 976 * @return a new instance of the implementation described by this 977 * {@code Service}. 978 * @throws NoSuchAlgorithmException 979 * if the instance could not be constructed. 980 * @throws InvalidParameterException 981 * if the implementation does not support the specified 982 * {@code constructorParameter}. 983 */ 984 public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException { 985 if (implementation == null || !className.equals(lastClassName)) { 986 ClassLoader cl = provider.getClass().getClassLoader(); 987 if (cl == null) { 988 cl = ClassLoader.getSystemClassLoader(); 989 } 990 try { 991 implementation = Class.forName(className, true, cl); 992 lastClassName = className; 993 } catch (Exception e) { 994 throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found: " + e); 995 } 996 } 997 if (constructorParameter == null) { 998 try { 999 return implementation.newInstance(); 1000 } catch (Exception e) { 1001 throw new NoSuchAlgorithmException( 1002 type + " " + algorithm + " implementation not found", e); 1003 } 1004 } 1005 if (!supportsParameter(constructorParameter)) { 1006 throw new InvalidParameterException(type + ": service cannot use the parameter"); 1007 } 1008 1009 Class[] parameterTypes = new Class[1]; 1010 Object[] initargs = { constructorParameter }; 1011 try { 1012 if (type.equalsIgnoreCase("CertStore")) { 1013 parameterTypes[0] = Class.forName("java.security.cert.CertStoreParameters"); 1014 } else { 1015 parameterTypes[0] = constructorParameter.getClass(); 1016 } 1017 return implementation.getConstructor(parameterTypes) 1018 .newInstance(initargs); 1019 } catch (Exception e) { 1020 throw new NoSuchAlgorithmException(type + " " + algorithm 1021 + " implementation not found", e); 1022 } 1023 } 1024 1025 /** 1026 * Indicates whether this {@code Service} supports the specified 1027 * constructor parameter. 1028 * 1029 * @param parameter 1030 * the parameter to test. 1031 * @return {@code true} if this {@code Service} supports the specified 1032 * constructor parameter, {@code false} otherwise. 1033 */ 1034 public boolean supportsParameter(Object parameter) { 1035 return true; 1036 } 1037 1038 /** 1039 * Returns a string containing a concise, human-readable description of 1040 * this {@code Service}. 1041 * 1042 * @return a printable representation for this {@code Service}. 1043 */ 1044 @Override 1045 public String toString() { 1046 String result = "Provider " + provider.getName() + " Service " 1047 + type + "." + algorithm + " " + className; 1048 if (aliases != null) { 1049 result = result + "\nAliases " + aliases.toString(); 1050 } 1051 if (attributes != null) { 1052 result = result + "\nAttributes " + attributes.toString(); 1053 } 1054 return result; 1055 } 1056 } 1057 1058 private void readObject(java.io.ObjectInputStream in) 1059 throws NotActiveException, IOException, ClassNotFoundException { 1060 in.defaultReadObject(); 1061 versionString = String.valueOf(version); 1062 providerNumber = -1; 1063 } 1064} 1065