/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.security;
import java.io.*;
import java.util.*;
import static java.util.Locale.ENGLISH;
import java.lang.ref.*;
import java.lang.reflect.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
This class represents a "provider" for the
Java Security API, where a provider implements some or all parts of
Java Security. Services that a provider may implement include:
- Algorithms (such as DSA, RSA, MD5 or SHA-1).
- Key generation, conversion, and management facilities (such as for
algorithm-specific keys).
Each provider has a name and a version number, and is configured
in each runtime it is installed in.
See The Provider Class
in the "Java Cryptography Architecture API Specification & Reference"
for information about how a particular type of provider, the
cryptographic service provider, works and is installed. However,
please note that a provider can be used to implement any security
service in Java that uses a pluggable architecture with a choice
of implementations that fit underneath.
Some provider implementations may encounter unrecoverable internal errors during their operation, for example a failure to communicate with a security token. A ProviderException
should be used to indicate such errors.
The service type Provider
is reserved for use by the security framework. Services of this type cannot be added, removed, or modified by applications. The following attributes are automatically placed in each Provider object:
Attributes Automatically Placed in a Provider Object
Name Value
Provider.id name
String.valueOf(provider.getName())
Provider.id version
String.valueOf(provider.getVersion())
Provider.id info
String.valueOf(provider.getInfo())
Provider.id className
provider.getClass().getName()
Author: Benjamin Renaud, Andreas Sterbenz
/**
* This class represents a "provider" for the
* Java Security API, where a provider implements some or all parts of
* Java Security. Services that a provider may implement include:
*
* <ul>
*
* <li>Algorithms (such as DSA, RSA, MD5 or SHA-1).
*
* <li>Key generation, conversion, and management facilities (such as for
* algorithm-specific keys).
*
*</ul>
*
* <p>Each provider has a name and a version number, and is configured
* in each runtime it is installed in.
*
* <p>See <a href =
* "../../../technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a>
* in the "Java Cryptography Architecture API Specification & Reference"
* for information about how a particular type of provider, the
* cryptographic service provider, works and is installed. However,
* please note that a provider can be used to implement any security
* service in Java that uses a pluggable architecture with a choice
* of implementations that fit underneath.
*
* <p>Some provider implementations may encounter unrecoverable internal
* errors during their operation, for example a failure to communicate with a
* security token. A {@link ProviderException} should be used to indicate
* such errors.
*
* <p>The service type {@code Provider} is reserved for use by the
* security framework. Services of this type cannot be added, removed,
* or modified by applications.
* The following attributes are automatically placed in each Provider object:
* <table cellspacing=4>
* <caption><b>Attributes Automatically Placed in a Provider Object</b></caption>
* <tr><th>Name</th><th>Value</th>
* <tr><td>{@code Provider.id name}</td>
* <td>{@code String.valueOf(provider.getName())}</td>
* <tr><td>{@code Provider.id version}</td>
* <td>{@code String.valueOf(provider.getVersion())}</td>
* <tr><td>{@code Provider.id info}</td>
<td>{@code String.valueOf(provider.getInfo())}</td>
* <tr><td>{@code Provider.id className}</td>
* <td>{@code provider.getClass().getName()}</td>
* </table>
*
* @author Benjamin Renaud
* @author Andreas Sterbenz
*/
public abstract class Provider extends Properties {
// Declare serialVersionUID to be compatible with JDK1.1
static final long serialVersionUID = -4298000515446427739L;
private static final sun.security.util.Debug debug =
sun.security.util.Debug.getInstance
("provider", "Provider");
The provider name.
@serial
/**
* The provider name.
*
* @serial
*/
private String name;
A description of the provider and its services.
@serial
/**
* A description of the provider and its services.
*
* @serial
*/
private String info;
The provider version number.
@serial
/**
* The provider version number.
*
* @serial
*/
private double version;
private transient Set<Map.Entry<Object,Object>> entrySet = null;
private transient int entrySetCallCount = 0;
private transient boolean initialized;
Constructs a provider with the specified name, version number,
and information.
Params: - name – the provider name.
- version – the provider version number.
- info – a description of the provider and its services.
/**
* Constructs a provider with the specified name, version number,
* and information.
*
* @param name the provider name.
*
* @param version the provider version number.
*
* @param info a description of the provider and its services.
*/
protected Provider(String name, double version, String info) {
this.name = name;
this.version = version;
this.info = info;
putId();
initialized = true;
}
Returns the name of this provider.
Returns: the name of this provider.
/**
* Returns the name of this provider.
*
* @return the name of this provider.
*/
public String getName() {
return name;
}
Returns the version number for this provider.
Returns: the version number for this provider.
/**
* Returns the version number for this provider.
*
* @return the version number for this provider.
*/
public double getVersion() {
return version;
}
Returns a human-readable description of the provider and its
services. This may return an HTML page, with relevant links.
Returns: a description of the provider and its services.
/**
* Returns a human-readable description of the provider and its
* services. This may return an HTML page, with relevant links.
*
* @return a description of the provider and its services.
*/
public String getInfo() {
return info;
}
Returns a string with the name and the version number
of this provider.
Returns: the string with the name and the version number
for this provider.
/**
* Returns a string with the name and the version number
* of this provider.
*
* @return the string with the name and the version number
* for this provider.
*/
public String toString() {
return name + " version " + version;
}
/*
* override the following methods to ensure that provider
* information can only be changed if the caller has the appropriate
* permissions.
*/
Clears this provider so that it no longer contains the properties
used to look up facilities implemented by the provider.
If a security manager is enabled, its checkSecurityAccess
method is called with the string "clearProviderProperties."+name
(where name
is the provider name) to see if it's ok to clear this provider.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to clear this provider
Since: 1.2
/**
* Clears this provider so that it no longer contains the properties
* used to look up facilities implemented by the provider.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "clearProviderProperties."+name}
* (where {@code name} is the provider name) to see if it's ok to clear
* this provider.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to clear this provider
*
* @since 1.2
*/
@Override
public synchronized void clear() {
check("clearProviderProperties."+name);
if (debug != null) {
debug.println("Remove " + name + " provider properties");
}
implClear();
}
Reads a property list (key and element pairs) from the input stream.
Params: - inStream – the input stream.
Throws: - IOException – if an error occurred when reading from the
input stream.
See Also:
/**
* Reads a property list (key and element pairs) from the input stream.
*
* @param inStream the input stream.
* @exception IOException if an error occurred when reading from the
* input stream.
* @see java.util.Properties#load
*/
@Override
public synchronized void load(InputStream inStream) throws IOException {
check("putProviderProperty."+name);
if (debug != null) {
debug.println("Load " + name + " provider properties");
}
Properties tempProperties = new Properties();
tempProperties.load(inStream);
implPutAll(tempProperties);
}
Copies all of the mappings from the specified Map to this provider.
These mappings will replace any properties that this provider had
for any of the keys currently in the specified Map.
Since: 1.2
/**
* Copies all of the mappings from the specified Map to this provider.
* These mappings will replace any properties that this provider had
* for any of the keys currently in the specified Map.
*
* @since 1.2
*/
@Override
public synchronized void putAll(Map<?,?> t) {
check("putProviderProperty."+name);
if (debug != null) {
debug.println("Put all " + name + " provider properties");
}
implPutAll(t);
}
Returns an unmodifiable Set view of the property entries contained
in this Provider.
See Also: - Entry
Since: 1.2
/**
* Returns an unmodifiable Set view of the property entries contained
* in this Provider.
*
* @see java.util.Map.Entry
* @since 1.2
*/
@Override
public synchronized Set<Map.Entry<Object,Object>> entrySet() {
checkInitialized();
if (entrySet == null) {
if (entrySetCallCount++ == 0) // Initial call
entrySet = Collections.unmodifiableMap(this).entrySet();
else
return super.entrySet(); // Recursive call
}
// This exception will be thrown if the implementation of
// Collections.unmodifiableMap.entrySet() is changed such that it
// no longer calls entrySet() on the backing Map. (Provider's
// entrySet implementation depends on this "implementation detail",
// which is unlikely to change.
if (entrySetCallCount != 2)
throw new RuntimeException("Internal error.");
return entrySet;
}
Returns an unmodifiable Set view of the property keys contained in
this provider.
Since: 1.2
/**
* Returns an unmodifiable Set view of the property keys contained in
* this provider.
*
* @since 1.2
*/
@Override
public Set<Object> keySet() {
checkInitialized();
return Collections.unmodifiableSet(super.keySet());
}
Returns an unmodifiable Collection view of the property values
contained in this provider.
Since: 1.2
/**
* Returns an unmodifiable Collection view of the property values
* contained in this provider.
*
* @since 1.2
*/
@Override
public Collection<Object> values() {
checkInitialized();
return Collections.unmodifiableCollection(super.values());
}
Sets the key
property to have the specified value
. If a security manager is enabled, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values.
Since: 1.2
/**
* Sets the {@code key} property to have the specified
* {@code value}.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "putProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to set this
* provider's property values.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values.
*
* @since 1.2
*/
@Override
public synchronized Object put(Object key, Object value) {
check("putProviderProperty."+name);
if (debug != null) {
debug.println("Set " + name + " provider property [" +
key + "/" + value +"]");
}
return implPut(key, value);
}
If the specified key is not already associated with a value (or is mapped to null
) associates it with the given value and returns null
, else returns the current value. If a security manager is enabled, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values.
Since: 1.8
/**
* If the specified key is not already associated with a value (or is mapped
* to {@code null}) associates it with the given value and returns
* {@code null}, else returns the current value.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "putProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to set this
* provider's property values.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values.
*
* @since 1.8
*/
@Override
public synchronized Object putIfAbsent(Object key, Object value) {
check("putProviderProperty."+name);
if (debug != null) {
debug.println("Set " + name + " provider property [" +
key + "/" + value +"]");
}
return implPutIfAbsent(key, value);
}
Removes the key
property (and its corresponding value
). If a security manager is enabled, its checkSecurityAccess
method is called with the string "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to remove this provider's properties.
Since: 1.2
/**
* Removes the {@code key} property (and its corresponding
* {@code value}).
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "removeProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to remove this
* provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to remove this provider's properties.
*
* @since 1.2
*/
@Override
public synchronized Object remove(Object key) {
check("removeProviderProperty."+name);
if (debug != null) {
debug.println("Remove " + name + " provider property " + key);
}
return implRemove(key);
}
Removes the entry for the specified key only if it is currently
mapped to the specified value.
If a security manager is enabled, its checkSecurityAccess
method is called with the string "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to remove this provider's properties.
Since: 1.8
/**
* Removes the entry for the specified key only if it is currently
* mapped to the specified value.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "removeProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to remove this
* provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to remove this provider's properties.
*
* @since 1.8
*/
@Override
public synchronized boolean remove(Object key, Object value) {
check("removeProviderProperty."+name);
if (debug != null) {
debug.println("Remove " + name + " provider property " + key);
}
return implRemove(key, value);
}
Replaces the entry for the specified key only if currently
mapped to the specified value.
If a security manager is enabled, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values.
Since: 1.8
/**
* Replaces the entry for the specified key only if currently
* mapped to the specified value.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "putProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to set this
* provider's property values.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values.
*
* @since 1.8
*/
@Override
public synchronized boolean replace(Object key, Object oldValue,
Object newValue) {
check("putProviderProperty." + name);
if (debug != null) {
debug.println("Replace " + name + " provider property " + key);
}
return implReplace(key, oldValue, newValue);
}
Replaces the entry for the specified key only if it is
currently mapped to some value.
If a security manager is enabled, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values.
Since: 1.8
/**
* Replaces the entry for the specified key only if it is
* currently mapped to some value.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "putProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to set this
* provider's property values.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values.
*
* @since 1.8
*/
@Override
public synchronized Object replace(Object key, Object value) {
check("putProviderProperty." + name);
if (debug != null) {
debug.println("Replace " + name + " provider property " + key);
}
return implReplace(key, value);
}
Replaces each entry's value with the result of invoking the given
function on that entry, in the order entries are returned by an entry
set iterator, until all entries have been processed or the function
throws an exception.
If a security manager is enabled, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values.
Since: 1.8
/**
* Replaces each entry's value with the result of invoking the given
* function on that entry, in the order entries are returned by an entry
* set iterator, until all entries have been processed or the function
* throws an exception.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the string {@code "putProviderProperty."+name},
* where {@code name} is the provider name, to see if it's ok to set this
* provider's property values.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values.
*
* @since 1.8
*/
@Override
public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
check("putProviderProperty." + name);
if (debug != null) {
debug.println("ReplaceAll " + name + " provider property ");
}
implReplaceAll(function);
}
Attempts to compute a mapping for the specified key and its current mapped value (or null
if there is no current mapping). If a security manager is enabled, its checkSecurityAccess
method is called with the strings "putProviderProperty."+name
and "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values and remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values or remove properties.
Since: 1.8
/**
* Attempts to compute a mapping for the specified key and its
* current mapped value (or {@code null} if there is no current
* mapping).
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the strings {@code "putProviderProperty."+name}
* and {@code "removeProviderProperty."+name}, where {@code name} is the
* provider name, to see if it's ok to set this provider's property values
* and remove this provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values or remove properties.
*
* @since 1.8
*/
@Override
public synchronized Object compute(Object key,
BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
check("putProviderProperty." + name);
check("removeProviderProperty" + name);
if (debug != null) {
debug.println("Compute " + name + " provider property " + key);
}
return implCompute(key, remappingFunction);
}
If the specified key is not already associated with a value (or is mapped to null
), attempts to compute its value using the given mapping function and enters it into this map unless null
. If a security manager is enabled, its checkSecurityAccess
method is called with the strings "putProviderProperty."+name
and "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values and remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values and remove properties.
Since: 1.8
/**
* If the specified key is not already associated with a value (or
* is mapped to {@code null}), attempts to compute its value using
* the given mapping function and enters it into this map unless
* {@code null}.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the strings {@code "putProviderProperty."+name}
* and {@code "removeProviderProperty."+name}, where {@code name} is the
* provider name, to see if it's ok to set this provider's property values
* and remove this provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values and remove properties.
*
* @since 1.8
*/
@Override
public synchronized Object computeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
check("putProviderProperty." + name);
check("removeProviderProperty" + name);
if (debug != null) {
debug.println("ComputeIfAbsent " + name + " provider property " +
key);
}
return implComputeIfAbsent(key, mappingFunction);
}
If the value for the specified key is present and non-null, attempts to
compute a new mapping given the key and its current mapped value.
If a security manager is enabled, its checkSecurityAccess
method is called with the strings "putProviderProperty."+name
and "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values and remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values or remove properties.
Since: 1.8
/**
* If the value for the specified key is present and non-null, attempts to
* compute a new mapping given the key and its current mapped value.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the strings {@code "putProviderProperty."+name}
* and {@code "removeProviderProperty."+name}, where {@code name} is the
* provider name, to see if it's ok to set this provider's property values
* and remove this provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values or remove properties.
*
* @since 1.8
*/
@Override
public synchronized Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
check("putProviderProperty." + name);
check("removeProviderProperty" + name);
if (debug != null) {
debug.println("ComputeIfPresent " + name + " provider property " +
key);
}
return implComputeIfPresent(key, remappingFunction);
}
If the specified key is not already associated with a value or is
associated with null, associates it with the given value. Otherwise,
replaces the value with the results of the given remapping function,
or removes if the result is null. This method may be of use when
combining multiple mapped values for a key.
If a security manager is enabled, its checkSecurityAccess
method is called with the strings "putProviderProperty."+name
and "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values and remove this provider's properties.
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values or remove properties.
Since: 1.8
/**
* If the specified key is not already associated with a value or is
* associated with null, associates it with the given value. Otherwise,
* replaces the value with the results of the given remapping function,
* or removes if the result is null. This method may be of use when
* combining multiple mapped values for a key.
*
* <p>If a security manager is enabled, its {@code checkSecurityAccess}
* method is called with the strings {@code "putProviderProperty."+name}
* and {@code "removeProviderProperty."+name}, where {@code name} is the
* provider name, to see if it's ok to set this provider's property values
* and remove this provider's properties.
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method
* denies access to set property values or remove properties.
*
* @since 1.8
*/
@Override
public synchronized Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
check("putProviderProperty." + name);
check("removeProviderProperty" + name);
if (debug != null) {
debug.println("Merge " + name + " provider property " + key);
}
return implMerge(key, value, remappingFunction);
}
// let javadoc show doc from superclass
@Override
public Object get(Object key) {
checkInitialized();
return super.get(key);
}
Since: 1.8
/**
* @since 1.8
*/
@Override
public synchronized Object getOrDefault(Object key, Object defaultValue) {
checkInitialized();
return super.getOrDefault(key, defaultValue);
}
Since: 1.8
/**
* @since 1.8
*/
@Override
public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
checkInitialized();
super.forEach(action);
}
// let javadoc show doc from superclass
@Override
public Enumeration<Object> keys() {
checkInitialized();
return super.keys();
}
// let javadoc show doc from superclass
@Override
public Enumeration<Object> elements() {
checkInitialized();
return super.elements();
}
// let javadoc show doc from superclass
public String getProperty(String key) {
checkInitialized();
return super.getProperty(key);
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException();
}
}
private void check(String directive) {
checkInitialized();
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkSecurityAccess(directive);
}
}
// legacy properties changed since last call to any services method?
private transient boolean legacyChanged;
// serviceMap changed since last call to getServices()
private transient boolean servicesChanged;
// Map<String,String>
private transient Map<String,String> legacyStrings;
// Map<ServiceKey,Service>
// used for services added via putService(), initialized on demand
private transient Map<ServiceKey,Service> serviceMap;
// Map<ServiceKey,Service>
// used for services added via legacy methods, init on demand
private transient Map<ServiceKey,Service> legacyMap;
// Set<Service>
// Unmodifiable set of all services. Initialized on demand.
private transient Set<Service> serviceSet;
// register the id attributes for this provider
// this is to ensure that equals() and hashCode() do not incorrectly
// report to different provider objects as the same
private void putId() {
// note: name and info may be null
super.put("Provider.id name", String.valueOf(name));
super.put("Provider.id version", String.valueOf(version));
super.put("Provider.id info", String.valueOf(info));
super.put("Provider.id className", this.getClass().getName());
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
Map<Object,Object> copy = new HashMap<>();
for (Map.Entry<Object,Object> entry : super.entrySet()) {
copy.put(entry.getKey(), entry.getValue());
}
defaults = null;
in.defaultReadObject();
implClear();
initialized = true;
putAll(copy);
}
private boolean checkLegacy(Object key) {
String keyString = (String)key;
if (keyString.startsWith("Provider.")) {
return false;
}
legacyChanged = true;
if (legacyStrings == null) {
legacyStrings = new LinkedHashMap<String,String>();
}
return true;
}
Copies all of the mappings from the specified Map to this provider.
Internal method to be called AFTER the security check has been
performed.
/**
* Copies all of the mappings from the specified Map to this provider.
* Internal method to be called AFTER the security check has been
* performed.
*/
private void implPutAll(Map<?,?> t) {
for (Map.Entry<?,?> e : t.entrySet()) {
implPut(e.getKey(), e.getValue());
}
}
private Object implRemove(Object key) {
if (key instanceof String) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.remove((String)key);
}
return super.remove(key);
}
private boolean implRemove(Object key, Object value) {
if (key instanceof String && value instanceof String) {
if (!checkLegacy(key)) {
return false;
}
legacyStrings.remove((String)key, value);
}
return super.remove(key, value);
}
private boolean implReplace(Object key, Object oldValue, Object newValue) {
if ((key instanceof String) && (oldValue instanceof String) &&
(newValue instanceof String)) {
if (!checkLegacy(key)) {
return false;
}
legacyStrings.replace((String)key, (String)oldValue,
(String)newValue);
}
return super.replace(key, oldValue, newValue);
}
private Object implReplace(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.replace((String)key, (String)value);
}
return super.replace(key, value);
}
private void implReplaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
legacyChanged = true;
if (legacyStrings == null) {
legacyStrings = new LinkedHashMap<String,String>();
} else {
legacyStrings.replaceAll((BiFunction<? super String, ? super String, ? extends String>) function);
}
super.replaceAll(function);
}
private Object implMerge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
if ((key instanceof String) && (value instanceof String)) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.merge((String)key, (String)value,
(BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
}
return super.merge(key, value, remappingFunction);
}
private Object implCompute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
if (key instanceof String) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.computeIfAbsent((String) key,
(Function<? super String, ? extends String>) remappingFunction);
}
return super.compute(key, remappingFunction);
}
private Object implComputeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
if (key instanceof String) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.computeIfAbsent((String) key,
(Function<? super String, ? extends String>) mappingFunction);
}
return super.computeIfAbsent(key, mappingFunction);
}
private Object implComputeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
if (key instanceof String) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.computeIfPresent((String) key,
(BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
}
return super.computeIfPresent(key, remappingFunction);
}
private Object implPut(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.put((String)key, (String)value);
}
return super.put(key, value);
}
private Object implPutIfAbsent(Object key, Object value) {
if ((key instanceof String) && (value instanceof String)) {
if (!checkLegacy(key)) {
return null;
}
legacyStrings.putIfAbsent((String)key, (String)value);
}
return super.putIfAbsent(key, value);
}
private void implClear() {
if (legacyStrings != null) {
legacyStrings.clear();
}
if (legacyMap != null) {
legacyMap.clear();
}
if (serviceMap != null) {
serviceMap.clear();
}
legacyChanged = false;
servicesChanged = false;
serviceSet = null;
super.clear();
putId();
}
// used as key in the serviceMap and legacyMap HashMaps
private static class ServiceKey {
private final String type;
private final String algorithm;
private final String originalAlgorithm;
private ServiceKey(String type, String algorithm, boolean intern) {
this.type = type;
this.originalAlgorithm = algorithm;
algorithm = algorithm.toUpperCase(ENGLISH);
this.algorithm = intern ? algorithm.intern() : algorithm;
}
public int hashCode() {
return type.hashCode() + algorithm.hashCode();
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ServiceKey == false) {
return false;
}
ServiceKey other = (ServiceKey)obj;
return this.type.equals(other.type)
&& this.algorithm.equals(other.algorithm);
}
boolean matches(String type, String algorithm) {
return (this.type == type) && (this.originalAlgorithm == algorithm);
}
}
Ensure all the legacy String properties are fully parsed into
service objects.
/**
* Ensure all the legacy String properties are fully parsed into
* service objects.
*/
private void ensureLegacyParsed() {
if ((legacyChanged == false) || (legacyStrings == null)) {
return;
}
serviceSet = null;
if (legacyMap == null) {
legacyMap = new LinkedHashMap<ServiceKey,Service>();
} else {
legacyMap.clear();
}
for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
parseLegacyPut(entry.getKey(), entry.getValue());
}
removeInvalidServices(legacyMap);
legacyChanged = false;
}
Remove all invalid services from the Map. Invalid services can only
occur if the legacy properties are inconsistent or incomplete.
/**
* Remove all invalid services from the Map. Invalid services can only
* occur if the legacy properties are inconsistent or incomplete.
*/
private void removeInvalidServices(Map<ServiceKey,Service> map) {
for (Iterator<Map.Entry<ServiceKey, Service>> t =
map.entrySet().iterator(); t.hasNext(); ) {
Service s = t.next().getValue();
if (s.isValid() == false) {
t.remove();
}
}
}
private String[] getTypeAndAlgorithm(String key) {
int i = key.indexOf(".");
if (i < 1) {
if (debug != null) {
debug.println("Ignoring invalid entry in provider "
+ name + ":" + key);
}
return null;
}
String type = key.substring(0, i);
String alg = key.substring(i + 1);
return new String[] {type, alg};
}
private final static String ALIAS_PREFIX = "Alg.Alias.";
private final static String ALIAS_PREFIX_LOWER = "alg.alias.";
private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
private void parseLegacyPut(String name, String value) {
if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
// e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
// aliasKey ~ MessageDigest.SHA
String stdAlg = value;
String aliasKey = name.substring(ALIAS_LENGTH);
String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
if (typeAndAlg == null) {
return;
}
String type = getEngineName(typeAndAlg[0]);
String aliasAlg = typeAndAlg[1].intern();
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
s.addAlias(aliasAlg);
} else {
String[] typeAndAlg = getTypeAndAlgorithm(name);
if (typeAndAlg == null) {
return;
}
int i = typeAndAlg[1].indexOf(' ');
if (i == -1) {
// e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
String type = getEngineName(typeAndAlg[0]);
String stdAlg = typeAndAlg[1].intern();
String className = value;
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
s.className = className;
} else { // attribute
// e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
String attributeValue = value;
String type = getEngineName(typeAndAlg[0]);
String attributeString = typeAndAlg[1];
String stdAlg = attributeString.substring(0, i).intern();
String attributeName = attributeString.substring(i + 1);
// kill additional spaces
while (attributeName.startsWith(" ")) {
attributeName = attributeName.substring(1);
}
attributeName = attributeName.intern();
ServiceKey key = new ServiceKey(type, stdAlg, true);
Service s = legacyMap.get(key);
if (s == null) {
s = new Service(this);
s.type = type;
s.algorithm = stdAlg;
legacyMap.put(key, s);
}
s.addAttribute(attributeName, attributeValue);
}
}
}
Get the service describing this Provider's implementation of the specified type of this algorithm or alias. If no such implementation exists, this method returns null. If there are two matching services, one added to this provider using putService()
and one added via put()
, the service added via putService()
is returned. Params: - type – the type of
service
requested (for example, MessageDigest
) - algorithm – the case insensitive algorithm name (or alternate alias) of the service requested (for example,
SHA-1
)
Throws: - NullPointerException – if type or algorithm is null
Returns: the service describing this Provider's matching service
or null if no such service exists Since: 1.5
/**
* Get the service describing this Provider's implementation of the
* specified type of this algorithm or alias. If no such
* implementation exists, this method returns null. If there are two
* matching services, one added to this provider using
* {@link #putService putService()} and one added via {@link #put put()},
* the service added via {@link #putService putService()} is returned.
*
* @param type the type of {@link Service service} requested
* (for example, {@code MessageDigest})
* @param algorithm the case insensitive algorithm name (or alternate
* alias) of the service requested (for example, {@code SHA-1})
*
* @return the service describing this Provider's matching service
* or null if no such service exists
*
* @throws NullPointerException if type or algorithm is null
*
* @since 1.5
*/
public synchronized Service getService(String type, String algorithm) {
checkInitialized();
// avoid allocating a new key object if possible
ServiceKey key = previousKey;
if (key.matches(type, algorithm) == false) {
key = new ServiceKey(type, algorithm, false);
previousKey = key;
}
if (serviceMap != null) {
Service service = serviceMap.get(key);
if (service != null) {
return service;
}
}
ensureLegacyParsed();
return (legacyMap != null) ? legacyMap.get(key) : null;
}
// ServiceKey from previous getService() call
// by re-using it if possible we avoid allocating a new object
// and the toUpperCase() call.
// re-use will occur e.g. as the framework traverses the provider
// list and queries each provider with the same values until it finds
// a matching service
private static volatile ServiceKey previousKey =
new ServiceKey("", "", false);
Get an unmodifiable Set of all services supported by
this Provider.
Returns: an unmodifiable Set of all services supported by
this Provider Since: 1.5
/**
* Get an unmodifiable Set of all services supported by
* this Provider.
*
* @return an unmodifiable Set of all services supported by
* this Provider
*
* @since 1.5
*/
public synchronized Set<Service> getServices() {
checkInitialized();
if (legacyChanged || servicesChanged) {
serviceSet = null;
}
if (serviceSet == null) {
ensureLegacyParsed();
Set<Service> set = new LinkedHashSet<>();
if (serviceMap != null) {
set.addAll(serviceMap.values());
}
if (legacyMap != null) {
set.addAll(legacyMap.values());
}
serviceSet = Collections.unmodifiableSet(set);
servicesChanged = false;
}
return serviceSet;
}
Add a service. If a service of the same type with the same algorithm name exists and it was added using putService()
, it is replaced by the new service. This method also places information about this service in the provider's Hashtable values in the format described in the
Java Cryptography Architecture API Specification & Reference .
Also, if there is a security manager, its checkSecurityAccess
method is called with the string "putProviderProperty."+name
, where name
is the provider name, to see if it's ok to set this provider's property values. If the default implementation of checkSecurityAccess
is used (that is, that method is not overriden), then this results in a call to the security manager's checkPermission
method with a SecurityPermission("putProviderProperty."+name)
permission.
Params: - s – the Service to add
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to set property values. - NullPointerException – if s is null
Since: 1.5
/**
* Add a service. If a service of the same type with the same algorithm
* name exists and it was added using {@link #putService putService()},
* it is replaced by the new service.
* This method also places information about this service
* in the provider's Hashtable values in the format described in the
* <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
* Java Cryptography Architecture API Specification & Reference </a>.
*
* <p>Also, if there is a security manager, its
* {@code checkSecurityAccess} method is called with the string
* {@code "putProviderProperty."+name}, where {@code name} is
* the provider name, to see if it's ok to set this provider's property
* values. If the default implementation of {@code checkSecurityAccess}
* is used (that is, that method is not overriden), then this results in
* a call to the security manager's {@code checkPermission} method with
* a {@code SecurityPermission("putProviderProperty."+name)}
* permission.
*
* @param s the Service to add
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method denies
* access to set property values.
* @throws NullPointerException if s is null
*
* @since 1.5
*/
protected synchronized void putService(Service s) {
check("putProviderProperty." + name);
if (debug != null) {
debug.println(name + ".putService(): " + s);
}
if (s == null) {
throw new NullPointerException();
}
if (s.getProvider() != this) {
throw new IllegalArgumentException
("service.getProvider() must match this Provider object");
}
if (serviceMap == null) {
serviceMap = new LinkedHashMap<ServiceKey,Service>();
}
servicesChanged = true;
String type = s.getType();
String algorithm = s.getAlgorithm();
ServiceKey key = new ServiceKey(type, algorithm, true);
// remove existing service
implRemoveService(serviceMap.get(key));
serviceMap.put(key, s);
for (String alias : s.getAliases()) {
serviceMap.put(new ServiceKey(type, alias, true), s);
}
putPropertyStrings(s);
}
Put the string properties for this Service in this Provider's
Hashtable.
/**
* Put the string properties for this Service in this Provider's
* Hashtable.
*/
private void putPropertyStrings(Service s) {
String type = s.getType();
String algorithm = s.getAlgorithm();
// use super() to avoid permission check and other processing
super.put(type + "." + algorithm, s.getClassName());
for (String alias : s.getAliases()) {
super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
}
for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
String key = type + "." + algorithm + " " + entry.getKey();
super.put(key, entry.getValue());
}
}
Remove the string properties for this Service from this Provider's
Hashtable.
/**
* Remove the string properties for this Service from this Provider's
* Hashtable.
*/
private void removePropertyStrings(Service s) {
String type = s.getType();
String algorithm = s.getAlgorithm();
// use super() to avoid permission check and other processing
super.remove(type + "." + algorithm);
for (String alias : s.getAliases()) {
super.remove(ALIAS_PREFIX + type + "." + alias);
}
for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
String key = type + "." + algorithm + " " + entry.getKey();
super.remove(key);
}
}
Remove a service previously added using putService()
. The specified service is removed from this provider. It will no longer be returned by getService()
and its information will be removed from this provider's Hashtable. Also, if there is a security manager, its checkSecurityAccess
method is called with the string "removeProviderProperty."+name
, where name
is the provider name, to see if it's ok to remove this provider's properties. If the default implementation of checkSecurityAccess
is used (that is, that method is not overriden), then this results in a call to the security manager's checkPermission
method with a SecurityPermission("removeProviderProperty."+name)
permission.
Params: - s – the Service to be removed
Throws: - SecurityException – if a security manager exists and its
SecurityManager.checkSecurityAccess
method denies access to remove this provider's properties. - NullPointerException – if s is null
Since: 1.5
/**
* Remove a service previously added using
* {@link #putService putService()}. The specified service is removed from
* this provider. It will no longer be returned by
* {@link #getService getService()} and its information will be removed
* from this provider's Hashtable.
*
* <p>Also, if there is a security manager, its
* {@code checkSecurityAccess} method is called with the string
* {@code "removeProviderProperty."+name}, where {@code name} is
* the provider name, to see if it's ok to remove this provider's
* properties. If the default implementation of
* {@code checkSecurityAccess} is used (that is, that method is not
* overriden), then this results in a call to the security manager's
* {@code checkPermission} method with a
* {@code SecurityPermission("removeProviderProperty."+name)}
* permission.
*
* @param s the Service to be removed
*
* @throws SecurityException
* if a security manager exists and its {@link
* java.lang.SecurityManager#checkSecurityAccess} method denies
* access to remove this provider's properties.
* @throws NullPointerException if s is null
*
* @since 1.5
*/
protected synchronized void removeService(Service s) {
check("removeProviderProperty." + name);
if (debug != null) {
debug.println(name + ".removeService(): " + s);
}
if (s == null) {
throw new NullPointerException();
}
implRemoveService(s);
}
private void implRemoveService(Service s) {
if ((s == null) || (serviceMap == null)) {
return;
}
String type = s.getType();
String algorithm = s.getAlgorithm();
ServiceKey key = new ServiceKey(type, algorithm, false);
Service oldService = serviceMap.get(key);
if (s != oldService) {
return;
}
servicesChanged = true;
serviceMap.remove(key);
for (String alias : s.getAliases()) {
serviceMap.remove(new ServiceKey(type, alias, false));
}
removePropertyStrings(s);
}
// Wrapped String that behaves in a case insensitive way for equals/hashCode
private static class UString {
final String string;
final String lowerString;
UString(String s) {
this.string = s;
this.lowerString = s.toLowerCase(ENGLISH);
}
public int hashCode() {
return lowerString.hashCode();
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof UString == false) {
return false;
}
UString other = (UString)obj;
return lowerString.equals(other.lowerString);
}
public String toString() {
return string;
}
}
// describe relevant properties of a type of engine
private static class EngineDescription {
final String name;
final boolean supportsParameter;
final String constructorParameterClassName;
private volatile Class<?> constructorParameterClass;
EngineDescription(String name, boolean sp, String paramName) {
this.name = name;
this.supportsParameter = sp;
this.constructorParameterClassName = paramName;
}
Class<?> getConstructorParameterClass() throws ClassNotFoundException {
Class<?> clazz = constructorParameterClass;
if (clazz == null) {
clazz = Class.forName(constructorParameterClassName);
constructorParameterClass = clazz;
}
return clazz;
}
}
// built in knowledge of the engine types shipped as part of the JDK
private static final Map<String,EngineDescription> knownEngines;
private static void addEngine(String name, boolean sp, String paramName) {
EngineDescription ed = new EngineDescription(name, sp, paramName);
// also index by canonical name to avoid toLowerCase() for some lookups
knownEngines.put(name.toLowerCase(ENGLISH), ed);
knownEngines.put(name, ed);
}
static {
knownEngines = new HashMap<String,EngineDescription>();
// JCA
addEngine("AlgorithmParameterGenerator", false, null);
addEngine("AlgorithmParameters", false, null);
addEngine("KeyFactory", false, null);
addEngine("KeyPairGenerator", false, null);
addEngine("KeyStore", false, null);
addEngine("MessageDigest", false, null);
addEngine("SecureRandom", false, null);
addEngine("Signature", true, null);
addEngine("CertificateFactory", false, null);
addEngine("CertPathBuilder", false, null);
addEngine("CertPathValidator", false, null);
addEngine("CertStore", false,
"java.security.cert.CertStoreParameters");
// JCE
addEngine("Cipher", true, null);
addEngine("ExemptionMechanism", false, null);
addEngine("Mac", true, null);
addEngine("KeyAgreement", true, null);
addEngine("KeyGenerator", false, null);
addEngine("SecretKeyFactory", false, null);
// JSSE
addEngine("KeyManagerFactory", false, null);
addEngine("SSLContext", false, null);
addEngine("TrustManagerFactory", false, null);
// JGSS
addEngine("GssApiMechanism", false, null);
// SASL
addEngine("SaslClientFactory", false, null);
addEngine("SaslServerFactory", false, null);
// POLICY
addEngine("Policy", false,
"java.security.Policy$Parameters");
// CONFIGURATION
addEngine("Configuration", false,
"javax.security.auth.login.Configuration$Parameters");
// XML DSig
addEngine("XMLSignatureFactory", false, null);
addEngine("KeyInfoFactory", false, null);
addEngine("TransformService", false, null);
// Smart Card I/O
addEngine("TerminalFactory", false,
"java.lang.Object");
}
// get the "standard" (mixed-case) engine name for arbitary case engine name
// if there is no known engine by that name, return s
private static String getEngineName(String s) {
// try original case first, usually correct
EngineDescription e = knownEngines.get(s);
if (e == null) {
e = knownEngines.get(s.toLowerCase(ENGLISH));
}
return (e == null) ? s : e.name;
}
The description of a security service. It encapsulates the properties
of a service and contains a factory method to obtain new implementation
instances of this service.
Each service has a provider that offers the service, a type,
an algorithm name, and the name of the class that implements the
service. Optionally, it also includes a list of alternate algorithm
names for this service (aliases) and attributes, which are a map of
(name, value) String pairs.
This class defines the methods
supportsParameter()
and newInstance()
which are used by the Java security framework when it searches for suitable services and instantiates them. The valid arguments to those methods depend on the type of service. For the service types defined within Java SE, see the
Java Cryptography Architecture API Specification & Reference
for the valid values.
Note that components outside of Java SE can define additional types of
services and their behavior.
Instances of this class are immutable.
Since: 1.5
/**
* The description of a security service. It encapsulates the properties
* of a service and contains a factory method to obtain new implementation
* instances of this service.
*
* <p>Each service has a provider that offers the service, a type,
* an algorithm name, and the name of the class that implements the
* service. Optionally, it also includes a list of alternate algorithm
* names for this service (aliases) and attributes, which are a map of
* (name, value) String pairs.
*
* <p>This class defines the methods {@link #supportsParameter
* supportsParameter()} and {@link #newInstance newInstance()}
* which are used by the Java security framework when it searches for
* suitable services and instantiates them. The valid arguments to those
* methods depend on the type of service. For the service types defined
* within Java SE, see the
* <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
* Java Cryptography Architecture API Specification & Reference </a>
* for the valid values.
* Note that components outside of Java SE can define additional types of
* services and their behavior.
*
* <p>Instances of this class are immutable.
*
* @since 1.5
*/
public static class Service {
private String type, algorithm, className;
private final Provider provider;
private List<String> aliases;
private Map<UString,String> attributes;
// Reference to the cached implementation Class object
private volatile Reference<Class<?>> classRef;
// flag indicating whether this service has its attributes for
// supportedKeyFormats or supportedKeyClasses set
// if null, the values have not been initialized
// if TRUE, at least one of supportedFormats/Classes is non null
private volatile Boolean hasKeyAttributes;
// supported encoding formats
private String[] supportedFormats;
// names of the supported key (super) classes
private Class[] supportedClasses;
// whether this service has been registered with the Provider
private boolean registered;
private static final Class<?>[] CLASS0 = new Class<?>[0];
// this constructor and these methods are used for parsing
// the legacy string properties.
private Service(Provider provider) {
this.provider = provider;
aliases = Collections.<String>emptyList();
attributes = Collections.<UString,String>emptyMap();
}
private boolean isValid() {
return (type != null) && (algorithm != null) && (className != null);
}
private void addAlias(String alias) {
if (aliases.isEmpty()) {
aliases = new ArrayList<String>(2);
}
aliases.add(alias);
}
void addAttribute(String type, String value) {
if (attributes.isEmpty()) {
attributes = new HashMap<UString,String>(8);
}
attributes.put(new UString(type), value);
}
Construct a new service.
Params: - provider – the provider that offers this service
- type – the type of this service
- algorithm – the algorithm name
- className – the name of the class implementing this service
- aliases – List of aliases or null if algorithm has no aliases
- attributes – Map of attributes or null if this implementation
has no attributes
Throws: - NullPointerException – if provider, type, algorithm, or
className is null
/**
* Construct a new service.
*
* @param provider the provider that offers this service
* @param type the type of this service
* @param algorithm the algorithm name
* @param className the name of the class implementing this service
* @param aliases List of aliases or null if algorithm has no aliases
* @param attributes Map of attributes or null if this implementation
* has no attributes
*
* @throws NullPointerException if provider, type, algorithm, or
* className is null
*/
public Service(Provider provider, String type, String algorithm,
String className, List<String> aliases,
Map<String,String> attributes) {
if ((provider == null) || (type == null) ||
(algorithm == null) || (className == null)) {
throw new NullPointerException();
}
this.provider = provider;
this.type = getEngineName(type);
this.algorithm = algorithm;
this.className = className;
if (aliases == null) {
this.aliases = Collections.<String>emptyList();
} else {
this.aliases = new ArrayList<String>(aliases);
}
if (attributes == null) {
this.attributes = Collections.<UString,String>emptyMap();
} else {
this.attributes = new HashMap<UString,String>();
for (Map.Entry<String,String> entry : attributes.entrySet()) {
this.attributes.put(new UString(entry.getKey()), entry.getValue());
}
}
}
Get the type of this service. For example, MessageDigest
. Returns: the type of this service
/**
* Get the type of this service. For example, {@code MessageDigest}.
*
* @return the type of this service
*/
public final String getType() {
return type;
}
Return the name of the algorithm of this service. For example, SHA-1
. Returns: the algorithm of this service
/**
* Return the name of the algorithm of this service. For example,
* {@code SHA-1}.
*
* @return the algorithm of this service
*/
public final String getAlgorithm() {
return algorithm;
}
Return the Provider of this service.
Returns: the Provider of this service
/**
* Return the Provider of this service.
*
* @return the Provider of this service
*/
public final Provider getProvider() {
return provider;
}
Return the name of the class implementing this service.
Returns: the name of the class implementing this service
/**
* Return the name of the class implementing this service.
*
* @return the name of the class implementing this service
*/
public final String getClassName() {
return className;
}
// internal only
private final List<String> getAliases() {
return aliases;
}
Return the value of the specified attribute or null if this
attribute is not set for this Service.
Params: - name – the name of the requested attribute
Throws: - NullPointerException – if name is null
Returns: the value of the specified attribute or null if the
attribute is not present
/**
* Return the value of the specified attribute or null if this
* attribute is not set for this Service.
*
* @param name the name of the requested attribute
*
* @return the value of the specified attribute or null if the
* attribute is not present
*
* @throws NullPointerException if name is null
*/
public final String getAttribute(String name) {
if (name == null) {
throw new NullPointerException();
}
return attributes.get(new UString(name));
}
Return a new instance of the implementation described by this
service. The security provider framework uses this method to
construct implementations. Applications will typically not need
to call it.
The default implementation uses reflection to invoke the
standard constructor for this type of service.
Security providers can override this method to implement
instantiation in a different way.
For details and the values of constructorParameter that are
valid for the various types of services see the
Java Cryptography Architecture API Specification &
Reference.
Params: - constructorParameter – the value to pass to the constructor,
or null if this type of service does not use a constructorParameter.
Throws: - InvalidParameterException – if the value of
constructorParameter is invalid for this type of service.
- NoSuchAlgorithmException – if instantiation failed for
any other reason.
Returns: a new implementation of this service
/**
* Return a new instance of the implementation described by this
* service. The security provider framework uses this method to
* construct implementations. Applications will typically not need
* to call it.
*
* <p>The default implementation uses reflection to invoke the
* standard constructor for this type of service.
* Security providers can override this method to implement
* instantiation in a different way.
* For details and the values of constructorParameter that are
* valid for the various types of services see the
* <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
* Java Cryptography Architecture API Specification &
* Reference</a>.
*
* @param constructorParameter the value to pass to the constructor,
* or null if this type of service does not use a constructorParameter.
*
* @return a new implementation of this service
*
* @throws InvalidParameterException if the value of
* constructorParameter is invalid for this type of service.
* @throws NoSuchAlgorithmException if instantiation failed for
* any other reason.
*/
public Object newInstance(Object constructorParameter)
throws NoSuchAlgorithmException {
if (registered == false) {
if (provider.getService(type, algorithm) != this) {
throw new NoSuchAlgorithmException
("Service not registered with Provider "
+ provider.getName() + ": " + this);
}
registered = true;
}
try {
EngineDescription cap = knownEngines.get(type);
if (cap == null) {
// unknown engine type, use generic code
// this is the code path future for non-core
// optional packages
return newInstanceGeneric(constructorParameter);
}
if (cap.constructorParameterClassName == null) {
if (constructorParameter != null) {
throw new InvalidParameterException
("constructorParameter not used with " + type
+ " engines");
}
Class<?> clazz = getImplClass();
Class<?>[] empty = {};
Constructor<?> con = clazz.getConstructor(empty);
return con.newInstance();
} else {
Class<?> paramClass = cap.getConstructorParameterClass();
if (constructorParameter != null) {
Class<?> argClass = constructorParameter.getClass();
if (paramClass.isAssignableFrom(argClass) == false) {
throw new InvalidParameterException
("constructorParameter must be instanceof "
+ cap.constructorParameterClassName.replace('$', '.')
+ " for engine type " + type);
}
}
Class<?> clazz = getImplClass();
Constructor<?> cons = clazz.getConstructor(paramClass);
return cons.newInstance(constructorParameter);
}
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (InvocationTargetException e) {
throw new NoSuchAlgorithmException
("Error constructing implementation (algorithm: "
+ algorithm + ", provider: " + provider.getName()
+ ", class: " + className + ")", e.getCause());
} catch (Exception e) {
throw new NoSuchAlgorithmException
("Error constructing implementation (algorithm: "
+ algorithm + ", provider: " + provider.getName()
+ ", class: " + className + ")", e);
}
}
// return the implementation Class object for this service
private Class<?> getImplClass() throws NoSuchAlgorithmException {
try {
Reference<Class<?>> ref = classRef;
Class<?> clazz = (ref == null) ? null : ref.get();
if (clazz == null) {
ClassLoader cl = provider.getClass().getClassLoader();
if (cl == null) {
clazz = Class.forName(className);
} else {
clazz = cl.loadClass(className);
}
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new NoSuchAlgorithmException
("class configured for " + type + " (provider: " +
provider.getName() + ") is not public.");
}
classRef = new WeakReference<Class<?>>(clazz);
}
return clazz;
} catch (ClassNotFoundException e) {
throw new NoSuchAlgorithmException
("class configured for " + type + " (provider: " +
provider.getName() + ") cannot be found.", e);
}
}
Generic code path for unknown engine types. Call the
no-args constructor if constructorParameter is null, otherwise
use the first matching constructor.
/**
* Generic code path for unknown engine types. Call the
* no-args constructor if constructorParameter is null, otherwise
* use the first matching constructor.
*/
private Object newInstanceGeneric(Object constructorParameter)
throws Exception {
Class<?> clazz = getImplClass();
if (constructorParameter == null) {
// create instance with public no-arg constructor if it exists
try {
Class<?>[] empty = {};
Constructor<?> con = clazz.getConstructor(empty);
return con.newInstance();
} catch (NoSuchMethodException e) {
throw new NoSuchAlgorithmException("No public no-arg "
+ "constructor found in class " + className);
}
}
Class<?> argClass = constructorParameter.getClass();
Constructor[] cons = clazz.getConstructors();
// find first public constructor that can take the
// argument as parameter
for (Constructor<?> con : cons) {
Class<?>[] paramTypes = con.getParameterTypes();
if (paramTypes.length != 1) {
continue;
}
if (paramTypes[0].isAssignableFrom(argClass) == false) {
continue;
}
return con.newInstance(constructorParameter);
}
throw new NoSuchAlgorithmException("No public constructor matching "
+ argClass.getName() + " found in class " + className);
}
Test whether this Service can use the specified parameter.
Returns false if this service cannot use the parameter. Returns
true if this service can use the parameter, if a fast test is
infeasible, or if the status is unknown.
The security provider framework uses this method with
some types of services to quickly exclude non-matching
implementations for consideration.
Applications will typically not need to call it.
For details and the values of parameter that are valid for the
various types of services see the top of this class and the
Java Cryptography Architecture API Specification &
Reference.
Security providers can override it to implement their own test.
Params: - parameter – the parameter to test
Throws: - InvalidParameterException – if the value of parameter is
invalid for this type of service or if this method cannot be
used with this type of service
Returns: false if this this service cannot use the specified
parameter; true if it can possibly use the parameter
/**
* Test whether this Service can use the specified parameter.
* Returns false if this service cannot use the parameter. Returns
* true if this service can use the parameter, if a fast test is
* infeasible, or if the status is unknown.
*
* <p>The security provider framework uses this method with
* some types of services to quickly exclude non-matching
* implementations for consideration.
* Applications will typically not need to call it.
*
* <p>For details and the values of parameter that are valid for the
* various types of services see the top of this class and the
* <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
* Java Cryptography Architecture API Specification &
* Reference</a>.
* Security providers can override it to implement their own test.
*
* @param parameter the parameter to test
*
* @return false if this this service cannot use the specified
* parameter; true if it can possibly use the parameter
*
* @throws InvalidParameterException if the value of parameter is
* invalid for this type of service or if this method cannot be
* used with this type of service
*/
public boolean supportsParameter(Object parameter) {
EngineDescription cap = knownEngines.get(type);
if (cap == null) {
// unknown engine type, return true by default
return true;
}
if (cap.supportsParameter == false) {
throw new InvalidParameterException("supportsParameter() not "
+ "used with " + type + " engines");
}
// allow null for keys without attributes for compatibility
if ((parameter != null) && (parameter instanceof Key == false)) {
throw new InvalidParameterException
("Parameter must be instanceof Key for engine " + type);
}
if (hasKeyAttributes() == false) {
return true;
}
if (parameter == null) {
return false;
}
Key key = (Key)parameter;
if (supportsKeyFormat(key)) {
return true;
}
if (supportsKeyClass(key)) {
return true;
}
return false;
}
Return whether this service has its Supported* properties for
keys defined. Parses the attributes if not yet initialized.
/**
* Return whether this service has its Supported* properties for
* keys defined. Parses the attributes if not yet initialized.
*/
private boolean hasKeyAttributes() {
Boolean b = hasKeyAttributes;
if (b == null) {
synchronized (this) {
String s;
s = getAttribute("SupportedKeyFormats");
if (s != null) {
supportedFormats = s.split("\\|");
}
s = getAttribute("SupportedKeyClasses");
if (s != null) {
String[] classNames = s.split("\\|");
List<Class<?>> classList =
new ArrayList<>(classNames.length);
for (String className : classNames) {
Class<?> clazz = getKeyClass(className);
if (clazz != null) {
classList.add(clazz);
}
}
supportedClasses = classList.toArray(CLASS0);
}
boolean bool = (supportedFormats != null)
|| (supportedClasses != null);
b = Boolean.valueOf(bool);
hasKeyAttributes = b;
}
}
return b.booleanValue();
}
// get the key class object of the specified name
private Class<?> getKeyClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
// ignore
}
try {
ClassLoader cl = provider.getClass().getClassLoader();
if (cl != null) {
return cl.loadClass(name);
}
} catch (ClassNotFoundException e) {
// ignore
}
return null;
}
private boolean supportsKeyFormat(Key key) {
if (supportedFormats == null) {
return false;
}
String format = key.getFormat();
if (format == null) {
return false;
}
for (String supportedFormat : supportedFormats) {
if (supportedFormat.equals(format)) {
return true;
}
}
return false;
}
private boolean supportsKeyClass(Key key) {
if (supportedClasses == null) {
return false;
}
Class<?> keyClass = key.getClass();
for (Class<?> clazz : supportedClasses) {
if (clazz.isAssignableFrom(keyClass)) {
return true;
}
}
return false;
}
Return a String representation of this service.
Returns: a String representation of this service.
/**
* Return a String representation of this service.
*
* @return a String representation of this service.
*/
public String toString() {
String aString = aliases.isEmpty()
? "" : "\r\n aliases: " + aliases.toString();
String attrs = attributes.isEmpty()
? "" : "\r\n attributes: " + attributes.toString();
return provider.getName() + ": " + type + "." + algorithm
+ " -> " + className + aString + attrs + "\r\n";
}
}
}