/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.security; import java.io.IOException; import java.io.InputStream; import java.io.NotActiveException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.harmony.security.fortress.Services; /** * {@code Provider} is the abstract superclass for all security providers in the * Java security infrastructure. */ public abstract class Provider extends Properties { private static final long serialVersionUID = -4298000515446427739L; private String name; private double version; // String representation of the provider version number. private transient String versionString; private String info; //The provider preference order number. // Equals -1 for non registered provider. private transient int providerNumber = -1; // Contains "Service.Algorithm" and Provider.Service classes added using // putService() private transient LinkedHashMap<String, Service> serviceTable; // Contains "Service.Alias" and Provider.Service classes added using // putService() private transient LinkedHashMap<String, Service> aliasTable; // Contains "Service.Algorithm" and Provider.Service classes added using // put() private transient LinkedHashMap<String, Service> propertyServiceTable; // Contains "Service.Alias" and Provider.Service classes added using put() private transient LinkedHashMap<String, Service> propertyAliasTable; // The properties changed via put() private transient LinkedHashMap<Object, Object> changedProperties; // For getService(String type, String algorithm) optimization: // previous result private transient Provider.Service returnedService; // previous parameters private transient String lastAlgorithm; // last name private transient String lastServiceName; // For getServices() optimization: private transient Set<Service> lastServicesSet; // For getService(String type) optimization: private transient String lastType; // last Service found by type private transient Provider.Service lastServicesByType; /** * Constructs a new instance of {@code Provider} with its name, version and * description. * * @param name * the name of the provider. * @param version * the version of the provider. * @param info * a description of the provider. */ protected Provider(String name, double version, String info) { this.name = name; this.version = version; this.info = info; versionString = String.valueOf(version); putProviderInfo(); } /** * Returns the name of this provider. * * @return the name of this provider. */ public String getName() { return name; } /** * Returns the version number for the services being provided. * * @return the version number for the services being provided. */ public double getVersion() { return version; } /** * Returns a description of the services being provided. * * @return a description of the services being provided. */ public String getInfo() { return info; } /** * Returns a string containing a concise, human-readable description of * this {@code Provider} including its name and its version. * * @return a printable representation for this {@code Provider}. */ @Override public String toString() { return name + " version " + version; } /** * Clears all properties used to look up services implemented by this * {@code Provider}. */ @Override public synchronized void clear() { super.clear(); if (serviceTable != null) { serviceTable.clear(); } if (propertyServiceTable != null) { propertyServiceTable.clear(); } if (aliasTable != null) { aliasTable.clear(); } if (propertyAliasTable != null) { propertyAliasTable.clear(); } changedProperties = null; putProviderInfo(); if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } servicesChanged(); } @Override public synchronized void load(InputStream inStream) throws IOException { Properties tmp = new Properties(); tmp.load(inStream); myPutAll(tmp); } /** * Copies all from the provided map to this {@code Provider}. * @param t * the mappings to copy to this provider. */ @Override public synchronized void putAll(Map<?,?> t) { myPutAll(t); } private void myPutAll(Map<?,?> t) { if (changedProperties == null) { changedProperties = new LinkedHashMap<Object, Object>(); } Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator(); Object key; Object value; while (it.hasNext()) { Map.Entry<?, ?> entry = it.next(); key = entry.getKey(); if (key instanceof String && ((String) key).startsWith("Provider.")) { // Provider service type is reserved continue; } value = entry.getValue(); super.put(key, value); if (changedProperties.remove(key) == null) { removeFromPropertyServiceTable(key); } changedProperties.put(key, value); } if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } } @Override public synchronized Set<Map.Entry<Object,Object>> entrySet() { return Collections.unmodifiableSet(super.entrySet()); } @Override public Set<Object> keySet() { return Collections.unmodifiableSet(super.keySet()); } @Override public Collection<Object> values() { return Collections.unmodifiableCollection(super.values()); } /** * Maps the specified {@code key} property name to the specified {@code * value}. * * @param key * the name of the property. * @param value * the value of the property. * @return the value that was previously mapped to the specified {@code key} * ,or {@code null} if it did not have one. */ @Override public synchronized Object put(Object key, Object value) { if (key instanceof String && ((String) key).startsWith("Provider.")) { // Provider service type is reserved return null; } if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } if (changedProperties != null && changedProperties.remove(key) == null) { removeFromPropertyServiceTable(key); } if (changedProperties == null) { changedProperties = new LinkedHashMap<Object, Object>(); } changedProperties.put(key, value); return super.put(key, value); } /** * Removes the specified {@code key} and its associated value from this * {@code Provider}. * * @param key * the name of the property * @return the value that was mapped to the specified {@code key} ,or * {@code null} if no mapping was present */ @Override public synchronized Object remove(Object key) { if (key instanceof String && ((String) key).startsWith("Provider.")) { // Provider service type is reserved return null; } if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } if (changedProperties != null && changedProperties.remove(key) == null) { removeFromPropertyServiceTable(key); if (changedProperties.size() == 0) { changedProperties = null; } } return super.remove(key); } /** * Returns true if this provider implements the given algorithm. Caller * must specify the cryptographic service and specify constraints via the * attribute name and value. * * @param serv * Crypto service. * @param alg * Algorithm or type. * @param attribute * The attribute name or {@code null}. * @param val * The attribute value. * @return */ boolean implementsAlg(String serv, String alg, String attribute, String val) { String servAlg = serv + "." + alg; String prop = getPropertyIgnoreCase(servAlg); if (prop == null) { alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); if (alg != null) { servAlg = serv + "." + alg; prop = getPropertyIgnoreCase(servAlg); } } if (prop != null) { if (attribute == null) { return true; } return checkAttribute(servAlg, attribute, val); } return false; } /** * Returns true if this provider has the same value as is given for the * given attribute */ private boolean checkAttribute(String servAlg, String attribute, String val) { String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute); if (attributeValue != null) { if (attribute.equalsIgnoreCase("KeySize")) { if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) { return true; } } else { // other attributes if (attributeValue.equalsIgnoreCase(val)) { return true; } } } return false; } /** * * Set the provider preference order number. * * @param n */ void setProviderNumber(int n) { providerNumber = n; } /** * * Get the provider preference order number. * * @return */ int getProviderNumber() { return providerNumber; } /** * Get the service of the specified type * */ synchronized Provider.Service getService(String type) { updatePropertyServiceTable(); if (lastServicesByType != null && type.equals(lastType)) { return lastServicesByType; } Provider.Service service; for (Iterator<Service> it = getServices().iterator(); it.hasNext();) { service = it.next(); if (type.equals(service.type)) { lastType = type; lastServicesByType = service; return service; } } return null; } /** * Returns the service with the specified {@code type} implementing the * specified {@code algorithm}, or {@code null} if no such implementation * exists. * <p> * If two services match the requested type and algorithm, the one added * with the {@link #putService(Service)} is returned (as opposed to the one * added via {@link #put(Object, Object)}. * * @param type * the type of the service (for example {@code KeyPairGenerator}) * @param algorithm * the algorithm name (case insensitive) * @return the requested service, or {@code null} if no such implementation * exists */ public synchronized Provider.Service getService(String type, String algorithm) { if (type == null) { throw new NullPointerException("type == null"); } else if (algorithm == null) { throw new NullPointerException("algorithm == null"); } if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) { return returnedService; } String key = key(type, algorithm); Object o = null; if (serviceTable != null) { o = serviceTable.get(key); } if (o == null && aliasTable != null) { o = aliasTable.get(key); } if (o == null) { updatePropertyServiceTable(); } if (o == null && propertyServiceTable != null) { o = propertyServiceTable.get(key); } if (o == null && propertyAliasTable != null) { o = propertyAliasTable.get(key); } if (o != null) { lastServiceName = type; lastAlgorithm = algorithm; returnedService = (Provider.Service) o; return returnedService; } return null; } /** * Returns an unmodifiable {@code Set} of all services registered by this * provider. * * @return an unmodifiable {@code Set} of all services registered by this * provider */ public synchronized Set<Provider.Service> getServices() { updatePropertyServiceTable(); if (lastServicesSet != null) { return lastServicesSet; } if (serviceTable != null) { lastServicesSet = new LinkedHashSet<Service>(serviceTable.values()); } else { lastServicesSet = new LinkedHashSet<Service>(); } if (propertyServiceTable != null) { lastServicesSet.addAll(propertyServiceTable.values()); } lastServicesSet = Collections.unmodifiableSet(lastServicesSet); return lastServicesSet; } /** * Adds a {@code Service} to this {@code Provider}. If a service with the * same name was registered via this method, it is replace. * * @param s * the {@code Service} to register */ protected synchronized void putService(Provider.Service s) { if (s == null) { throw new NullPointerException("s == null"); } if ("Provider".equals(s.getType())) { // Provider service type cannot be added return; } servicesChanged(); if (serviceTable == null) { serviceTable = new LinkedHashMap<String, Service>(128); } serviceTable.put(key(s.type, s.algorithm), s); if (s.aliases != null) { if (aliasTable == null) { aliasTable = new LinkedHashMap<String, Service>(256); } for (String alias : s.getAliases()) { aliasTable.put(key(s.type, alias), s); } } serviceInfoToProperties(s); } /** * Removes a previously registered {@code Service} from this {@code * Provider}. * * @param s * the {@code Service} to remove * @throws NullPointerException * if {@code s} is {@code null} */ protected synchronized void removeService(Provider.Service s) { if (s == null) { throw new NullPointerException("s == null"); } servicesChanged(); if (serviceTable != null) { serviceTable.remove(key(s.type, s.algorithm)); } if (aliasTable != null && s.aliases != null) { for (String alias: s.getAliases()) { aliasTable.remove(key(s.type, alias)); } } serviceInfoFromProperties(s); } /** * Add Service information to the provider's properties. */ private void serviceInfoToProperties(Provider.Service s) { super.put(s.type + "." + s.algorithm, s.className); if (s.aliases != null) { for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); } } if (s.attributes != null) { for (Map.Entry<String, String> entry : s.attributes.entrySet()) { super.put(s.type + "." + s.algorithm + " " + entry.getKey(), entry.getValue()); } } if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } } /** * Remove Service information from the provider's properties. */ private void serviceInfoFromProperties(Provider.Service s) { super.remove(s.type + "." + s.algorithm); if (s.aliases != null) { for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { super.remove("Alg.Alias." + s.type + "." + i.next()); } } if (s.attributes != null) { for (Map.Entry<String, String> entry : s.attributes.entrySet()) { super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); } } if (providerNumber != -1) { // if registered then refresh Services Services.setNeedRefresh(); } } // Remove property information from provider Services private void removeFromPropertyServiceTable(Object key) { if (key == null || !(key instanceof String)) { return; } String k = (String) key; if (k.startsWith("Provider.")) { // Provider service type is reserved return; } Provider.Service s; String serviceName; String algorithm = null; String attribute = null; int i; if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> String aliasName; String service_alias = k.substring(10); i = service_alias.indexOf('.'); serviceName = service_alias.substring(0, i); aliasName = service_alias.substring(i + 1); if (propertyAliasTable != null) { propertyAliasTable.remove(key(serviceName, aliasName)); } if (propertyServiceTable != null) { for (Iterator<Service> it = propertyServiceTable.values().iterator(); it .hasNext();) { s = it.next(); if (s.aliases.contains(aliasName)) { s.aliases.remove(aliasName); return; } } } return; } int j = k.indexOf('.'); if (j == -1) { // unknown format return; } i = k.indexOf(' '); if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> serviceName = k.substring(0, j); algorithm = k.substring(j + 1); if (propertyServiceTable != null) { Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm)); if (ser != null && propertyAliasTable != null && ser.aliases != null) { for (String alias : ser.aliases) { propertyAliasTable.remove(key(serviceName, alias)); } } } } else { // <crypto_service>.<algorithm_or_type> // <attribute_name>=<attrValue> attribute = k.substring(i + 1); serviceName = k.substring(0, j); algorithm = k.substring(j + 1, i); if (propertyServiceTable != null) { Object o = propertyServiceTable.get(key(serviceName, algorithm)); if (o != null) { s = (Provider.Service) o; s.attributes.remove(attribute); } } } } // Update provider Services if the properties was changed private void updatePropertyServiceTable() { Object _key; Object _value; Provider.Service s; String serviceName; String algorithm; if (changedProperties == null || changedProperties.isEmpty()) { return; } for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it .hasNext();) { Map.Entry<Object, Object> entry = it.next(); _key = entry.getKey(); _value = entry.getValue(); if (_key == null || _value == null || !(_key instanceof String) || !(_value instanceof String)) { continue; } String key = (String) _key; String value = (String) _value; if (key.startsWith("Provider")) { // Provider service type is reserved continue; } int i; if (key.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> String aliasName; String service_alias = key.substring(10); i = service_alias.indexOf('.'); serviceName = service_alias.substring(0, i); aliasName = service_alias.substring(i + 1); algorithm = value; String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable == null) { propertyServiceTable = new LinkedHashMap<String, Service>(128); } else { o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; s.addAlias(aliasName); if (propertyAliasTable == null) { propertyAliasTable = new LinkedHashMap<String, Service>(256); } propertyAliasTable.put(key(serviceName, aliasName), s); } else { String className = (String) changedProperties .get(serviceName + "." + algorithm); if (className != null) { List<String> l = new ArrayList<String>(); l.add(aliasName); s = new Provider.Service(this, serviceName, algorithm, className, l, new HashMap<String, String>()); propertyServiceTable.put(propertyServiceTableKey, s); if (propertyAliasTable == null) { propertyAliasTable = new LinkedHashMap<String, Service>(256); } propertyAliasTable.put(key(serviceName, aliasName), s); } } continue; } int j = key.indexOf('.'); if (j == -1) { // unknown format continue; } i = key.indexOf(' '); if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> serviceName = key.substring(0, j); algorithm = key.substring(j + 1); String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable != null) { o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; s.className = value; } else { s = new Provider.Service(this, serviceName, algorithm, value, Collections.<String>emptyList(), Collections.<String,String>emptyMap()); if (propertyServiceTable == null) { propertyServiceTable = new LinkedHashMap<String, Service>(128); } propertyServiceTable.put(propertyServiceTableKey, s); } } else { // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue> serviceName = key.substring(0, j); algorithm = key.substring(j + 1, i); String attribute = key.substring(i + 1); String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable != null) { o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; s.putAttribute(attribute, value); } else { String className = (String) changedProperties .get(serviceName + "." + algorithm); if (className != null) { Map<String, String> m = new HashMap<String, String>(); m.put(attribute, value); s = new Provider.Service(this, serviceName, algorithm, className, new ArrayList<String>(), m); if (propertyServiceTable == null) { propertyServiceTable = new LinkedHashMap<String, Service>(128); } propertyServiceTable.put(propertyServiceTableKey, s); } } } } servicesChanged(); changedProperties = null; } private void servicesChanged() { lastServicesByType = null; lastServiceName = null; lastServicesSet = null; } /** * These attributes should be placed in each Provider object: * Provider.id name, Provider.id version, Provider.id info, * Provider.id className */ private void putProviderInfo() { super.put("Provider.id name", (name != null) ? name : "null"); super.put("Provider.id version", versionString); super.put("Provider.id info", (info != null) ? info : "null"); super.put("Provider.id className", this.getClass().getName()); } /** * Returns the property with the specified key in the provider properties. * The name is not case-sensitive. */ private String getPropertyIgnoreCase(String key) { String res = getProperty(key); if (res != null) { return res; } for (Enumeration<?> e = propertyNames(); e.hasMoreElements(); ) { String propertyName = (String) e.nextElement(); if (key.equalsIgnoreCase(propertyName)) { return getProperty(propertyName); } } return null; } private static String key(String type, String algorithm) { return type + '.' + algorithm.toUpperCase(Locale.US); } /** * {@code Service} represents a service in the Java Security infrastructure. * Each service describes its type, the algorithm it implements, to which * provider it belongs and other properties. */ public static class Service { /** Attribute name of supported key classes. */ private static final String ATTR_SUPPORTED_KEY_CLASSES = "SupportedKeyClasses"; /** Attribute name of supported key formats. */ private static final String ATTR_SUPPORTED_KEY_FORMATS = "SupportedKeyFormats"; /** Whether this type supports calls to {@link #supportsParameter(Object)}. */ private static final HashMap<String, Boolean> supportsParameterTypes = new HashMap<String, Boolean>(); static { // Does not support parameter supportsParameterTypes.put("AlgorithmParameterGenerator", false); supportsParameterTypes.put("AlgorithmParameters", false); supportsParameterTypes.put("CertificateFactory", false); supportsParameterTypes.put("CertPathBuilder", false); supportsParameterTypes.put("CertPathValidator", false); supportsParameterTypes.put("CertStore", false); supportsParameterTypes.put("KeyFactory", false); supportsParameterTypes.put("KeyGenerator", false); supportsParameterTypes.put("KeyManagerFactory", false); supportsParameterTypes.put("KeyPairGenerator", false); supportsParameterTypes.put("KeyStore", false); supportsParameterTypes.put("MessageDigest", false); supportsParameterTypes.put("SecretKeyFactory", false); supportsParameterTypes.put("SecureRandom", false); supportsParameterTypes.put("SSLContext", false); supportsParameterTypes.put("TrustManagerFactory", false); // Supports parameter supportsParameterTypes.put("Cipher", true); supportsParameterTypes.put("KeyAgreement", true); supportsParameterTypes.put("Mac", true); supportsParameterTypes.put("Signature", true); } /** Constructor argument classes for calls to {@link #newInstance(Object)}. */ private static final HashMap<String, Class<?>> constructorParameterClasses = new HashMap<String, Class<?>>(); static { // Types that take a parameter to newInstance constructorParameterClasses.put("CertStore", loadClassOrThrow("java.security.cert.CertStoreParameters")); // Types that do not take any kind of parameter constructorParameterClasses.put("AlgorithmParameterGenerator", null); constructorParameterClasses.put("AlgorithmParameters", null); constructorParameterClasses.put("CertificateFactory", null); constructorParameterClasses.put("CertPathBuilder", null); constructorParameterClasses.put("CertPathValidator", null); constructorParameterClasses.put("KeyFactory", null); constructorParameterClasses.put("KeyGenerator", null); constructorParameterClasses.put("KeyManagerFactory", null); constructorParameterClasses.put("KeyPairGenerator", null); constructorParameterClasses.put("KeyStore", null); constructorParameterClasses.put("MessageDigest", null); constructorParameterClasses.put("SecretKeyFactory", null); constructorParameterClasses.put("SecureRandom", null); constructorParameterClasses.put("SSLContext", null); constructorParameterClasses.put("TrustManagerFactory", null); constructorParameterClasses.put("Cipher", null); constructorParameterClasses.put("KeyAgreement", null); constructorParameterClasses.put("Mac", null); constructorParameterClasses.put("Signature", null); } /** Called to load a class if it's critical that the class exists. */ private static Class<?> loadClassOrThrow(String className) { try { return Provider.class.getClassLoader().loadClass(className); } catch (Exception e) { throw new AssertionError(e); } } // The provider private Provider provider; // The type of this service private String type; // The algorithm name private String algorithm; // The class implementing this service private String className; // The aliases private List<String> aliases; // The attributes private Map<String,String> attributes; // Service implementation private Class<?> implementation; // For newInstance() optimization private String lastClassName; /** Indicates whether supportedKeyClasses and supportedKeyFormats. */ private volatile boolean supportedKeysInitialized; /** List of classes that this service supports. */ private Class<?>[] keyClasses; /** List of key formats this service supports. */ private String[] keyFormats; /** * Constructs a new instance of {@code Service} with the given * attributes. * * @param provider * the provider to which this service belongs. * @param type * the type of this service (for example {@code * KeyPairGenerator}). * @param algorithm * the algorithm this service implements. * @param className * the name of the class implementing this service. * @param aliases * {@code List} of aliases for the algorithm name, or {@code * null} if the implemented algorithm has no aliases. * @param attributes * {@code Map} of additional attributes, or {@code null} if * this {@code Service} has no attributed. * @throws NullPointerException * if {@code provider, type, algorithm} or {@code className} * is {@code null}. */ public Service(Provider provider, String type, String algorithm, String className, List<String> aliases, Map<String, String> attributes) { if (provider == null) { throw new NullPointerException("provider == null"); } else if (type == null) { throw new NullPointerException("type == null"); } else if (algorithm == null) { throw new NullPointerException("algorithm == null"); } else if (className == null) { throw new NullPointerException("className == null"); } this.provider = provider; this.type = type; this.algorithm = algorithm; this.className = className; this.aliases = ((aliases != null) && (aliases.size() == 0)) ? Collections.<String>emptyList() : aliases; this.attributes = ((attributes != null) && (attributes.size() == 0)) ? Collections.<String,String>emptyMap() : attributes; } /** * Adds an alias. * * @param alias the alias to add */ /*package*/ void addAlias(String alias) { if ((aliases == null) || (aliases.size() == 0)) { aliases = new ArrayList<String>(); } aliases.add(alias); } /** * Puts a new attribute mapping. * * @param name the attribute name. * @param value the attribute value. */ /*package*/ void putAttribute(String name, String value) { if ((attributes == null) || (attributes.size() == 0)) { attributes = new HashMap<String,String>(); } attributes.put(name, value); } /** * Returns the type of this {@code Service}. For example {@code * KeyPairGenerator}. * * @return the type of this {@code Service}. */ public final String getType() { return type; } /** * Returns the name of the algorithm implemented by this {@code * Service}. * * @return the name of the algorithm implemented by this {@code * Service}. */ public final String getAlgorithm() { return algorithm; } /** * Returns the {@code Provider} this {@code Service} belongs to. * * @return the {@code Provider} this {@code Service} belongs to. */ public final Provider getProvider() { return provider; } /** * Returns the name of the class implementing this {@code Service}. * * @return the name of the class implementing this {@code Service}. */ public final String getClassName() { return className; } /** * Returns the value of the attribute with the specified {@code name}. * * @param name * the name of the attribute. * @return the value of the attribute, or {@code null} if no attribute * with the given name is set. * @throws NullPointerException * if {@code name} is {@code null}. */ public final String getAttribute(String name) { if (name == null) { throw new NullPointerException("name == null"); } if (attributes == null) { return null; } return attributes.get(name); } List<String> getAliases() { if (aliases == null){ aliases = new ArrayList<String>(0); } return aliases; } /** * Creates and returns a new instance of the implementation described by * this {@code Service}. * * @param constructorParameter * the parameter that is used by the constructor, or {@code * null} if the implementation does not declare a constructor * parameter. * @return a new instance of the implementation described by this * {@code Service}. * @throws NoSuchAlgorithmException * if the instance could not be constructed. * @throws InvalidParameterException * if the implementation does not support the specified * {@code constructorParameter}. */ public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException { if (implementation == null || !className.equals(lastClassName)) { ClassLoader cl = provider.getClass().getClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } try { implementation = Class.forName(className, true, cl); lastClassName = className; } catch (Exception e) { throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found: " + e); } } // We don't know whether this takes a parameter or not. if (!constructorParameterClasses.containsKey(type)) { if (constructorParameter == null) { return newInstanceNoParameter(); } else { return newInstanceWithParameter(constructorParameter, constructorParameter.getClass()); } } // A known type, but it's not required to have a parameter even if a // class is specified. if (constructorParameter == null) { return newInstanceNoParameter(); } // Make sure the provided constructor class is valid. final Class<?> expectedClass = constructorParameterClasses.get(type); if (expectedClass == null) { throw new IllegalArgumentException("Constructor parameter not supported for " + type); } if (!expectedClass.isAssignableFrom(constructorParameter.getClass())) { throw new IllegalArgumentException("Expecting constructor parameter of type " + expectedClass.getName() + " but was " + constructorParameter.getClass().getName()); } return newInstanceWithParameter(constructorParameter, expectedClass); } private Object newInstanceWithParameter(Object constructorParameter, Class<?> parameterClass) throws NoSuchAlgorithmException { try { Class<?>[] parameterTypes = { parameterClass }; Object[] initargs = { constructorParameter }; return implementation.getConstructor(parameterTypes).newInstance(initargs); } catch (Exception e) { throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found", e); } } private Object newInstanceNoParameter() throws NoSuchAlgorithmException { try { return implementation.newInstance(); } catch (Exception e) { throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found", e); } } /** * Indicates whether this {@code Service} supports the specified * constructor parameter. * * @param parameter * the parameter to test. * @return {@code true} if this {@code Service} supports the specified * constructor parameter, {@code false} otherwise. */ public boolean supportsParameter(Object parameter) { Boolean supportsParameter = supportsParameterTypes.get(type); if (supportsParameter == null) { return true; } if (!supportsParameter) { throw new InvalidParameterException("Cannot use a parameter with " + type); } /* * Only Key parameters are allowed, but allow null since there might * not be any listed classes or formats for this instance. */ if (parameter != null && !(parameter instanceof Key)) { throw new InvalidParameterException("Parameter should be of type Key"); } ensureSupportedKeysInitialized(); // No restriction specified by Provider registration. if (keyClasses == null && keyFormats == null) { return true; } // Restriction specified by registration, so null is not acceptable. if (parameter == null) { return false; } Key keyParam = (Key) parameter; if (keyClasses != null && isInArray(keyClasses, keyParam.getClass())) { return true; } if (keyFormats != null && isInArray(keyFormats, keyParam.getFormat())) { return true; } return false; } /** * Initialize the list of supported key classes and formats. */ private void ensureSupportedKeysInitialized() { if (supportedKeysInitialized) { return; } final String supportedClassesString = getAttribute(ATTR_SUPPORTED_KEY_CLASSES); if (supportedClassesString != null) { String[] keyClassNames = supportedClassesString.split("\\|"); ArrayList<Class<?>> supportedClassList = new ArrayList<Class<?>>( keyClassNames.length); final ClassLoader classLoader = getProvider().getClass().getClassLoader(); for (String keyClassName : keyClassNames) { try { Class<?> keyClass = classLoader.loadClass(keyClassName); if (Key.class.isAssignableFrom(keyClass)) { supportedClassList.add(keyClass); } } catch (ClassNotFoundException ignored) { } } keyClasses = supportedClassList.toArray(new Class<?>[supportedClassList.size()]); } final String supportedFormatString = getAttribute(ATTR_SUPPORTED_KEY_FORMATS); if (supportedFormatString != null) { keyFormats = supportedFormatString.split("\\|"); } supportedKeysInitialized = true; } /** * Check if an item is in the array. The array of supported key classes * and formats is usually just a length of 1, so a simple array is * faster than a Set. */ private static <T> boolean isInArray(T[] itemList, T target) { if (target == null) { return false; } for (T item : itemList) { if (target.equals(item)) { return true; } } return false; } /** * Check if an item is in the array. The array of supported key classes * and formats is usually just a length of 1, so a simple array is * faster than a Set. */ private static boolean isInArray(Class<?>[] itemList, Class<?> target) { if (target == null) { return false; } for (Class<?> item : itemList) { if (item.isAssignableFrom(target)) { return true; } } return false; } /** * Returns a string containing a concise, human-readable description of * this {@code Service}. * * @return a printable representation for this {@code Service}. */ @Override public String toString() { String result = "Provider " + provider.getName() + " Service " + type + "." + algorithm + " " + className; if (aliases != null) { result = result + "\nAliases " + aliases.toString(); } if (attributes != null) { result = result + "\nAttributes " + attributes.toString(); } return result; } } private void readObject(java.io.ObjectInputStream in) throws NotActiveException, IOException, ClassNotFoundException { in.defaultReadObject(); versionString = String.valueOf(version); providerNumber = -1; } }