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