0N/A/*
3909N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jca;
0N/A
0N/Aimport java.util.*;
0N/A
0N/Aimport java.security.*;
0N/Aimport java.security.Provider.Service;
0N/A
0N/A/**
0N/A * List of Providers. Used to represent the provider preferences.
0N/A *
0N/A * The system starts out with a ProviderList that only has the classNames
0N/A * of the Providers. Providers are loaded on demand only when needed.
0N/A *
0N/A * For compatibility reasons, Providers that could not be loaded are ignored
0N/A * and internally presented as the instance EMPTY_PROVIDER. However, those
0N/A * objects cannot be presented to applications. Call the convert() method
0N/A * to force all Providers to be loaded and to obtain a ProviderList with
0N/A * invalid entries removed. All this is handled by the Security class.
0N/A *
0N/A * Note that all indices used by this class are 0-based per general Java
0N/A * convention. These must be converted to the 1-based indices used by the
0N/A * Security class externally when needed.
0N/A *
0N/A * Instances of this class are immutable. This eliminates the need for
0N/A * cloning and synchronization in consumers. The add() and remove() style
0N/A * methods are static in order to avoid confusion about the immutability.
0N/A *
0N/A * @author Andreas Sterbenz
0N/A * @since 1.5
0N/A */
0N/Apublic final class ProviderList {
0N/A
0N/A final static sun.security.util.Debug debug =
0N/A sun.security.util.Debug.getInstance("jca", "ProviderList");
0N/A
0N/A private final static ProviderConfig[] PC0 = new ProviderConfig[0];
0N/A
0N/A private final static Provider[] P0 = new Provider[0];
0N/A
0N/A // constant for an ProviderList with no elements
0N/A static final ProviderList EMPTY = new ProviderList(PC0, true);
0N/A
0N/A // dummy provider object to use during initialization
0N/A // used to avoid explicit null checks in various places
0N/A private static final Provider EMPTY_PROVIDER =
0N/A new Provider("##Empty##", 1.0d, "initialization in progress") {
0N/A // override getService() to return null slightly faster
0N/A public Service getService(String type, String algorithm) {
0N/A return null;
0N/A }
0N/A };
0N/A
0N/A // construct a ProviderList from the security properties
0N/A // (static provider configuration in the java.security file)
0N/A static ProviderList fromSecurityProperties() {
0N/A // doPrivileged() because of Security.getProperty()
0N/A return AccessController.doPrivileged(
0N/A new PrivilegedAction<ProviderList>() {
0N/A public ProviderList run() {
0N/A return new ProviderList();
0N/A }
0N/A });
0N/A }
0N/A
0N/A public static ProviderList add(ProviderList providerList, Provider p) {
0N/A return insertAt(providerList, p, -1);
0N/A }
0N/A
0N/A public static ProviderList insertAt(ProviderList providerList, Provider p,
0N/A int position) {
0N/A if (providerList.getProvider(p.getName()) != null) {
0N/A return providerList;
0N/A }
3384N/A List<ProviderConfig> list = new ArrayList<>
0N/A (Arrays.asList(providerList.configs));
0N/A int n = list.size();
0N/A if ((position < 0) || (position > n)) {
0N/A position = n;
0N/A }
0N/A list.add(position, new ProviderConfig(p));
0N/A return new ProviderList(list.toArray(PC0), true);
0N/A }
0N/A
0N/A public static ProviderList remove(ProviderList providerList, String name) {
0N/A // make sure provider exists
0N/A if (providerList.getProvider(name) == null) {
0N/A return providerList;
0N/A }
0N/A // copy all except matching to new list
0N/A ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1];
0N/A int j = 0;
0N/A for (ProviderConfig config : providerList.configs) {
0N/A if (config.getProvider().getName().equals(name) == false) {
0N/A configs[j++] = config;
0N/A }
0N/A }
0N/A return new ProviderList(configs, true);
0N/A }
0N/A
0N/A // Create a new ProviderList from the specified Providers.
0N/A // This method is for use by SunJSSE.
0N/A public static ProviderList newList(Provider ... providers) {
0N/A ProviderConfig[] configs = new ProviderConfig[providers.length];
0N/A for (int i = 0; i < providers.length; i++) {
0N/A configs[i] = new ProviderConfig(providers[i]);
0N/A }
0N/A return new ProviderList(configs, true);
0N/A }
0N/A
0N/A // configuration of the providers
0N/A private final ProviderConfig[] configs;
0N/A
0N/A // flag indicating whether all configs have been loaded successfully
0N/A private volatile boolean allLoaded;
0N/A
0N/A // List returned by providers()
0N/A private final List<Provider> userList = new AbstractList<Provider>() {
0N/A public int size() {
0N/A return configs.length;
0N/A }
0N/A public Provider get(int index) {
0N/A return getProvider(index);
0N/A }
0N/A };
0N/A
0N/A /**
0N/A * Create a new ProviderList from an array of configs
0N/A */
0N/A private ProviderList(ProviderConfig[] configs, boolean allLoaded) {
0N/A this.configs = configs;
0N/A this.allLoaded = allLoaded;
0N/A }
0N/A
0N/A /**
0N/A * Return a new ProviderList parsed from the java.security Properties.
0N/A */
0N/A private ProviderList() {
3384N/A List<ProviderConfig> configList = new ArrayList<>();
0N/A for (int i = 1; true; i++) {
0N/A String entry = Security.getProperty("security.provider." + i);
0N/A if (entry == null) {
0N/A break;
0N/A }
0N/A entry = entry.trim();
0N/A if (entry.length() == 0) {
0N/A System.err.println("invalid entry for " +
0N/A "security.provider." + i);
0N/A break;
0N/A }
0N/A int k = entry.indexOf(' ');
0N/A ProviderConfig config;
0N/A if (k == -1) {
0N/A config = new ProviderConfig(entry);
0N/A } else {
0N/A String className = entry.substring(0, k);
0N/A String argument = entry.substring(k + 1).trim();
0N/A config = new ProviderConfig(className, argument);
0N/A }
0N/A
0N/A // Get rid of duplicate providers.
0N/A if (configList.contains(config) == false) {
0N/A configList.add(config);
0N/A }
0N/A }
0N/A configs = configList.toArray(PC0);
0N/A if (debug != null) {
0N/A debug.println("provider configuration: " + configList);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Construct a special ProviderList for JAR verification. It consists
0N/A * of the providers specified via jarClassNames, which must be on the
0N/A * bootclasspath and cannot be in signed JAR files. This is to avoid
0N/A * possible recursion and deadlock during verification.
0N/A */
0N/A ProviderList getJarList(String[] jarClassNames) {
3384N/A List<ProviderConfig> newConfigs = new ArrayList<>();
0N/A for (String className : jarClassNames) {
0N/A ProviderConfig newConfig = new ProviderConfig(className);
0N/A for (ProviderConfig config : configs) {
0N/A // if the equivalent object is present in this provider list,
0N/A // use the old object rather than the new object.
0N/A // this ensures that when the provider is loaded in the
0N/A // new thread local list, it will also become available
0N/A // in this provider list
0N/A if (config.equals(newConfig)) {
0N/A newConfig = config;
0N/A break;
0N/A }
0N/A }
0N/A newConfigs.add(newConfig);
0N/A }
0N/A ProviderConfig[] configArray = newConfigs.toArray(PC0);
0N/A return new ProviderList(configArray, false);
0N/A }
0N/A
0N/A public int size() {
0N/A return configs.length;
0N/A }
0N/A
0N/A /**
0N/A * Return the Provider at the specified index. Returns EMPTY_PROVIDER
0N/A * if the provider could not be loaded at this time.
0N/A */
0N/A Provider getProvider(int index) {
0N/A Provider p = configs[index].getProvider();
0N/A return (p != null) ? p : EMPTY_PROVIDER;
0N/A }
0N/A
0N/A /**
0N/A * Return an unmodifiable List of all Providers in this List. The
0N/A * individual Providers are loaded on demand. Elements that could not
0N/A * be initialized are replaced with EMPTY_PROVIDER.
0N/A */
0N/A public List<Provider> providers() {
0N/A return userList;
0N/A }
0N/A
0N/A private ProviderConfig getProviderConfig(String name) {
0N/A int index = getIndex(name);
0N/A return (index != -1) ? configs[index] : null;
0N/A }
0N/A
0N/A // return the Provider with the specified name or null
0N/A public Provider getProvider(String name) {
0N/A ProviderConfig config = getProviderConfig(name);
0N/A return (config == null) ? null : config.getProvider();
0N/A }
0N/A
0N/A /**
0N/A * Return the index at which the provider with the specified name is
0N/A * installed or -1 if it is not present in this ProviderList.
0N/A */
0N/A public int getIndex(String name) {
0N/A for (int i = 0; i < configs.length; i++) {
0N/A Provider p = getProvider(i);
0N/A if (p.getName().equals(name)) {
0N/A return i;
0N/A }
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A // attempt to load all Providers not already loaded
0N/A private int loadAll() {
0N/A if (allLoaded) {
0N/A return configs.length;
0N/A }
0N/A if (debug != null) {
0N/A debug.println("Loading all providers");
0N/A new Exception("Call trace").printStackTrace();
0N/A }
0N/A int n = 0;
0N/A for (int i = 0; i < configs.length; i++) {
0N/A Provider p = configs[i].getProvider();
0N/A if (p != null) {
0N/A n++;
0N/A }
0N/A }
0N/A if (n == configs.length) {
0N/A allLoaded = true;
0N/A }
0N/A return n;
0N/A }
0N/A
0N/A /**
0N/A * Try to load all Providers and return the ProviderList. If one or
0N/A * more Providers could not be loaded, a new ProviderList with those
0N/A * entries removed is returned. Otherwise, the method returns this.
0N/A */
0N/A ProviderList removeInvalid() {
0N/A int n = loadAll();
0N/A if (n == configs.length) {
0N/A return this;
0N/A }
0N/A ProviderConfig[] newConfigs = new ProviderConfig[n];
0N/A for (int i = 0, j = 0; i < configs.length; i++) {
0N/A ProviderConfig config = configs[i];
0N/A if (config.isLoaded()) {
0N/A newConfigs[j++] = config;
0N/A }
0N/A }
0N/A return new ProviderList(newConfigs, true);
0N/A }
0N/A
0N/A // return the providers as an array
0N/A public Provider[] toArray() {
0N/A return providers().toArray(P0);
0N/A }
0N/A
0N/A // return a String representation of this ProviderList
0N/A public String toString() {
0N/A return Arrays.asList(configs).toString();
0N/A }
0N/A
0N/A /**
0N/A * Return a Service describing an implementation of the specified
0N/A * algorithm from the Provider with the highest precedence that
0N/A * supports that algorithm. Return null if no Provider supports this
0N/A * algorithm.
0N/A */
0N/A public Service getService(String type, String name) {
0N/A for (int i = 0; i < configs.length; i++) {
0N/A Provider p = getProvider(i);
0N/A Service s = p.getService(type, name);
0N/A if (s != null) {
0N/A return s;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Return a List containing all the Services describing implementations
0N/A * of the specified algorithms in precedence order. If no implementation
0N/A * exists, this method returns an empty List.
0N/A *
0N/A * The elements of this list are determined lazily on demand.
0N/A *
0N/A * The List returned is NOT thread safe.
0N/A */
0N/A public List<Service> getServices(String type, String algorithm) {
0N/A return new ServiceList(type, algorithm);
0N/A }
0N/A
0N/A /**
0N/A * This method exists for compatibility with JCE only. It will be removed
0N/A * once JCE has been changed to use the replacement method.
0N/A * @deprecated use getServices(List<ServiceId>) instead
0N/A */
0N/A @Deprecated
0N/A public List<Service> getServices(String type, List<String> algorithms) {
3384N/A List<ServiceId> ids = new ArrayList<>();
0N/A for (String alg : algorithms) {
0N/A ids.add(new ServiceId(type, alg));
0N/A }
0N/A return getServices(ids);
0N/A }
0N/A
0N/A public List<Service> getServices(List<ServiceId> ids) {
0N/A return new ServiceList(ids);
0N/A }
0N/A
0N/A /**
0N/A * Inner class for a List of Services. Custom List implementation in
0N/A * order to delay Provider initialization and lookup.
0N/A * Not thread safe.
0N/A */
0N/A private final class ServiceList extends AbstractList<Service> {
0N/A
0N/A // type and algorithm for simple lookup
0N/A // avoid allocating/traversing the ServiceId list for these lookups
0N/A private final String type;
0N/A private final String algorithm;
0N/A
0N/A // list of ids for parallel lookup
0N/A // if ids is non-null, type and algorithm are null
0N/A private final List<ServiceId> ids;
0N/A
0N/A // first service we have found
0N/A // it is stored in a separate variable so that we can avoid
0N/A // allocating the services list if we do not need the second service.
0N/A // this is the case if we don't failover (failovers are typically rare)
0N/A private Service firstService;
0N/A
0N/A // list of the services we have found so far
0N/A private List<Service> services;
0N/A
0N/A // index into config[] of the next provider we need to query
0N/A private int providerIndex;
0N/A
0N/A ServiceList(String type, String algorithm) {
0N/A this.type = type;
0N/A this.algorithm = algorithm;
0N/A this.ids = null;
0N/A }
0N/A
0N/A ServiceList(List<ServiceId> ids) {
0N/A this.type = null;
0N/A this.algorithm = null;
0N/A this.ids = ids;
0N/A }
0N/A
0N/A private void addService(Service s) {
0N/A if (firstService == null) {
0N/A firstService = s;
0N/A } else {
0N/A if (services == null) {
0N/A services = new ArrayList<Service>(4);
0N/A services.add(firstService);
0N/A }
0N/A services.add(s);
0N/A }
0N/A }
0N/A
0N/A private Service tryGet(int index) {
0N/A while (true) {
0N/A if ((index == 0) && (firstService != null)) {
0N/A return firstService;
0N/A } else if ((services != null) && (services.size() > index)) {
0N/A return services.get(index);
0N/A }
0N/A if (providerIndex >= configs.length) {
0N/A return null;
0N/A }
0N/A // check all algorithms in this provider before moving on
0N/A Provider p = getProvider(providerIndex++);
0N/A if (type != null) {
0N/A // simple lookup
0N/A Service s = p.getService(type, algorithm);
0N/A if (s != null) {
0N/A addService(s);
0N/A }
0N/A } else {
0N/A // parallel lookup
0N/A for (ServiceId id : ids) {
0N/A Service s = p.getService(id.type, id.algorithm);
0N/A if (s != null) {
0N/A addService(s);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A public Service get(int index) {
0N/A Service s = tryGet(index);
0N/A if (s == null) {
0N/A throw new IndexOutOfBoundsException();
0N/A }
0N/A return s;
0N/A }
0N/A
0N/A public int size() {
0N/A int n;
0N/A if (services != null) {
0N/A n = services.size();
0N/A } else {
0N/A n = (firstService != null) ? 1 : 0;
0N/A }
0N/A while (tryGet(n) != null) {
0N/A n++;
0N/A }
0N/A return n;
0N/A }
0N/A
0N/A // override isEmpty() and iterator() to not call size()
0N/A // this avoids loading + checking all Providers
0N/A
0N/A public boolean isEmpty() {
0N/A return (tryGet(0) == null);
0N/A }
0N/A
0N/A public Iterator<Service> iterator() {
0N/A return new Iterator<Service>() {
0N/A int index;
0N/A
0N/A public boolean hasNext() {
0N/A return tryGet(index) != null;
0N/A }
0N/A
0N/A public Service next() {
0N/A Service s = tryGet(index);
0N/A if (s == null) {
0N/A throw new NoSuchElementException();
0N/A }
0N/A index++;
0N/A return s;
0N/A }
0N/A
0N/A public void remove() {
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A };
0N/A }
0N/A }
0N/A
0N/A}