ProviderList.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.jca;
27
28import java.util.*;
29
30import java.security.*;
31import java.security.Provider.Service;
32
33/**
34 * List of Providers. Used to represent the provider preferences.
35 *
36 * The system starts out with a ProviderList that only has the classNames
37 * of the Providers. Providers are loaded on demand only when needed.
38 *
39 * For compatibility reasons, Providers that could not be loaded are ignored
40 * and internally presented as the instance EMPTY_PROVIDER. However, those
41 * objects cannot be presented to applications. Call the convert() method
42 * to force all Providers to be loaded and to obtain a ProviderList with
43 * invalid entries removed. All this is handled by the Security class.
44 *
45 * Note that all indices used by this class are 0-based per general Java
46 * convention. These must be converted to the 1-based indices used by the
47 * Security class externally when needed.
48 *
49 * Instances of this class are immutable. This eliminates the need for
50 * cloning and synchronization in consumers. The add() and remove() style
51 * methods are static in order to avoid confusion about the immutability.
52 *
53 * @author  Andreas Sterbenz
54 * @since   1.5
55 */
56public final class ProviderList {
57
58    static final sun.security.util.Debug debug =
59        sun.security.util.Debug.getInstance("jca", "ProviderList");
60
61    private static final ProviderConfig[] PC0 = new ProviderConfig[0];
62
63    private static final Provider[] P0 = new Provider[0];
64
65    // constant for an ProviderList with no elements
66    static final ProviderList EMPTY = new ProviderList(PC0, true);
67
68    // dummy provider object to use during initialization
69    // used to avoid explicit null checks in various places
70    private static final Provider EMPTY_PROVIDER =
71        new Provider("##Empty##", 1.0d, "initialization in progress") {
72            private static final long serialVersionUID = 1151354171352296389L;
73            // override getService() to return null slightly faster
74            public Service getService(String type, String algorithm) {
75                return null;
76            }
77        };
78
79    // construct a ProviderList from the security properties
80    // (static provider configuration in the java.security file)
81    static ProviderList fromSecurityProperties() {
82        // doPrivileged() because of Security.getProperty()
83        return AccessController.doPrivileged(
84                        new PrivilegedAction<ProviderList>() {
85            public ProviderList run() {
86                return new ProviderList();
87            }
88        });
89    }
90
91    public static ProviderList add(ProviderList providerList, Provider p) {
92        return insertAt(providerList, p, -1);
93    }
94
95    public static ProviderList insertAt(ProviderList providerList, Provider p,
96            int position) {
97        if (providerList.getProvider(p.getName()) != null) {
98            return providerList;
99        }
100        List<ProviderConfig> list = new ArrayList<>
101                                    (Arrays.asList(providerList.configs));
102        int n = list.size();
103        if ((position < 0) || (position > n)) {
104            position = n;
105        }
106        list.add(position, new ProviderConfig(p));
107        return new ProviderList(list.toArray(PC0), true);
108    }
109
110    public static ProviderList remove(ProviderList providerList, String name) {
111        // make sure provider exists
112        if (providerList.getProvider(name) == null) {
113            return providerList;
114        }
115        // copy all except matching to new list
116        ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1];
117        int j = 0;
118        for (ProviderConfig config : providerList.configs) {
119            if (config.getProvider().getName().equals(name) == false) {
120                configs[j++] = config;
121            }
122        }
123        return new ProviderList(configs, true);
124    }
125
126    // Create a new ProviderList from the specified Providers.
127    // This method is for use by SunJSSE.
128    public static ProviderList newList(Provider ... providers) {
129        ProviderConfig[] configs = new ProviderConfig[providers.length];
130        for (int i = 0; i < providers.length; i++) {
131            configs[i] = new ProviderConfig(providers[i]);
132        }
133        return new ProviderList(configs, true);
134    }
135
136    // configuration of the providers
137    private final ProviderConfig[] configs;
138
139    // flag indicating whether all configs have been loaded successfully
140    private volatile boolean allLoaded;
141
142    // List returned by providers()
143    private final List<Provider> userList = new AbstractList<Provider>() {
144        public int size() {
145            return configs.length;
146        }
147        public Provider get(int index) {
148            return getProvider(index);
149        }
150    };
151
152    /**
153     * Create a new ProviderList from an array of configs
154     */
155    private ProviderList(ProviderConfig[] configs, boolean allLoaded) {
156        this.configs = configs;
157        this.allLoaded = allLoaded;
158    }
159
160    /**
161     * Return a new ProviderList parsed from the java.security Properties.
162     */
163    private ProviderList() {
164        List<ProviderConfig> configList = new ArrayList<>();
165        for (int i = 1; true; i++) {
166            String entry = Security.getProperty("security.provider." + i);
167            if (entry == null) {
168                break;
169            }
170            entry = entry.trim();
171            if (entry.length() == 0) {
172                System.err.println("invalid entry for " +
173                                   "security.provider." + i);
174                break;
175            }
176            int k = entry.indexOf(' ');
177            ProviderConfig config;
178            if (k == -1) {
179                config = new ProviderConfig(entry);
180            } else {
181                String provName = entry.substring(0, k);
182                String argument = entry.substring(k + 1).trim();
183                config = new ProviderConfig(provName, argument);
184            }
185
186            // Get rid of duplicate providers.
187            if (configList.contains(config) == false) {
188                configList.add(config);
189            }
190        }
191        configs = configList.toArray(PC0);
192        if (debug != null) {
193            debug.println("provider configuration: " + configList);
194        }
195    }
196
197    /**
198     * Construct a special ProviderList for JAR verification. It consists
199     * of the providers specified via jarClassNames, which must be on the
200     * bootclasspath and cannot be in signed JAR files. This is to avoid
201     * possible recursion and deadlock during verification.
202     */
203    ProviderList getJarList(String[] jarProvNames) {
204        List<ProviderConfig> newConfigs = new ArrayList<>();
205        for (String provName : jarProvNames) {
206            ProviderConfig newConfig = new ProviderConfig(provName);
207            for (ProviderConfig config : configs) {
208                // if the equivalent object is present in this provider list,
209                // use the old object rather than the new object.
210                // this ensures that when the provider is loaded in the
211                // new thread local list, it will also become available
212                // in this provider list
213                if (config.equals(newConfig)) {
214                    newConfig = config;
215                    break;
216                }
217            }
218            newConfigs.add(newConfig);
219        }
220        ProviderConfig[] configArray = newConfigs.toArray(PC0);
221        return new ProviderList(configArray, false);
222    }
223
224    public int size() {
225        return configs.length;
226    }
227
228    /**
229     * Return the Provider at the specified index. Returns EMPTY_PROVIDER
230     * if the provider could not be loaded at this time.
231     */
232    Provider getProvider(int index) {
233        Provider p = configs[index].getProvider();
234        return (p != null) ? p : EMPTY_PROVIDER;
235    }
236
237    /**
238     * Return an unmodifiable List of all Providers in this List. The
239     * individual Providers are loaded on demand. Elements that could not
240     * be initialized are replaced with EMPTY_PROVIDER.
241     */
242    public List<Provider> providers() {
243        return userList;
244    }
245
246    private ProviderConfig getProviderConfig(String name) {
247        int index = getIndex(name);
248        return (index != -1) ? configs[index] : null;
249    }
250
251    // return the Provider with the specified name or null
252    public Provider getProvider(String name) {
253        ProviderConfig config = getProviderConfig(name);
254        return (config == null) ? null : config.getProvider();
255    }
256
257    /**
258     * Return the index at which the provider with the specified name is
259     * installed or -1 if it is not present in this ProviderList.
260     */
261    public int getIndex(String name) {
262        for (int i = 0; i < configs.length; i++) {
263            Provider p = getProvider(i);
264            if (p.getName().equals(name)) {
265                return i;
266            }
267        }
268        return -1;
269    }
270
271    // attempt to load all Providers not already loaded
272    private int loadAll() {
273        if (allLoaded) {
274            return configs.length;
275        }
276        if (debug != null) {
277            debug.println("Loading all providers");
278            new Exception("Call trace").printStackTrace();
279        }
280        int n = 0;
281        for (int i = 0; i < configs.length; i++) {
282            Provider p = configs[i].getProvider();
283            if (p != null) {
284                n++;
285            }
286        }
287        if (n == configs.length) {
288            allLoaded = true;
289        }
290        return n;
291    }
292
293    /**
294     * Try to load all Providers and return the ProviderList. If one or
295     * more Providers could not be loaded, a new ProviderList with those
296     * entries removed is returned. Otherwise, the method returns this.
297     */
298    ProviderList removeInvalid() {
299        int n = loadAll();
300        if (n == configs.length) {
301            return this;
302        }
303        ProviderConfig[] newConfigs = new ProviderConfig[n];
304        for (int i = 0, j = 0; i < configs.length; i++) {
305            ProviderConfig config = configs[i];
306            if (config.isLoaded()) {
307                newConfigs[j++] = config;
308            }
309        }
310        return new ProviderList(newConfigs, true);
311    }
312
313    // return the providers as an array
314    public Provider[] toArray() {
315        return providers().toArray(P0);
316    }
317
318    // return a String representation of this ProviderList
319    public String toString() {
320        return Arrays.asList(configs).toString();
321    }
322
323    /**
324     * Return a Service describing an implementation of the specified
325     * algorithm from the Provider with the highest precedence that
326     * supports that algorithm. Return null if no Provider supports this
327     * algorithm.
328     */
329    public Service getService(String type, String name) {
330        for (int i = 0; i < configs.length; i++) {
331            Provider p = getProvider(i);
332            Service s = p.getService(type, name);
333            if (s != null) {
334                return s;
335            }
336        }
337        return null;
338    }
339
340    /**
341     * Return a List containing all the Services describing implementations
342     * of the specified algorithms in precedence order. If no implementation
343     * exists, this method returns an empty List.
344     *
345     * The elements of this list are determined lazily on demand.
346     *
347     * The List returned is NOT thread safe.
348     */
349    public List<Service> getServices(String type, String algorithm) {
350        return new ServiceList(type, algorithm);
351    }
352
353    /**
354     * This method exists for compatibility with JCE only. It will be removed
355     * once JCE has been changed to use the replacement method.
356     * @deprecated use {@code getServices(List<ServiceId>)} instead
357     */
358    @Deprecated
359    public List<Service> getServices(String type, List<String> algorithms) {
360        List<ServiceId> ids = new ArrayList<>();
361        for (String alg : algorithms) {
362            ids.add(new ServiceId(type, alg));
363        }
364        return getServices(ids);
365    }
366
367    public List<Service> getServices(List<ServiceId> ids) {
368        return new ServiceList(ids);
369    }
370
371    /**
372     * Inner class for a List of Services. Custom List implementation in
373     * order to delay Provider initialization and lookup.
374     * Not thread safe.
375     */
376    private final class ServiceList extends AbstractList<Service> {
377
378        // type and algorithm for simple lookup
379        // avoid allocating/traversing the ServiceId list for these lookups
380        private final String type;
381        private final String algorithm;
382
383        // list of ids for parallel lookup
384        // if ids is non-null, type and algorithm are null
385        private final List<ServiceId> ids;
386
387        // first service we have found
388        // it is stored in a separate variable so that we can avoid
389        // allocating the services list if we do not need the second service.
390        // this is the case if we don't failover (failovers are typically rare)
391        private Service firstService;
392
393        // list of the services we have found so far
394        private List<Service> services;
395
396        // index into config[] of the next provider we need to query
397        private int providerIndex;
398
399        ServiceList(String type, String algorithm) {
400            this.type = type;
401            this.algorithm = algorithm;
402            this.ids = null;
403        }
404
405        ServiceList(List<ServiceId> ids) {
406            this.type = null;
407            this.algorithm = null;
408            this.ids = ids;
409        }
410
411        private void addService(Service s) {
412            if (firstService == null) {
413                firstService = s;
414            } else {
415                if (services == null) {
416                    services = new ArrayList<Service>(4);
417                    services.add(firstService);
418                }
419                services.add(s);
420            }
421        }
422
423        private Service tryGet(int index) {
424            while (true) {
425                if ((index == 0) && (firstService != null)) {
426                    return firstService;
427                } else if ((services != null) && (services.size() > index)) {
428                    return services.get(index);
429                }
430                if (providerIndex >= configs.length) {
431                    return null;
432                }
433                // check all algorithms in this provider before moving on
434                Provider p = getProvider(providerIndex++);
435                if (type != null) {
436                    // simple lookup
437                    Service s = p.getService(type, algorithm);
438                    if (s != null) {
439                        addService(s);
440                    }
441                } else {
442                    // parallel lookup
443                    for (ServiceId id : ids) {
444                        Service s = p.getService(id.type, id.algorithm);
445                        if (s != null) {
446                            addService(s);
447                        }
448                    }
449                }
450            }
451        }
452
453        public Service get(int index) {
454            Service s = tryGet(index);
455            if (s == null) {
456                throw new IndexOutOfBoundsException();
457            }
458            return s;
459        }
460
461        public int size() {
462            int n;
463            if (services != null) {
464                n = services.size();
465            } else {
466                n = (firstService != null) ? 1 : 0;
467            }
468            while (tryGet(n) != null) {
469                n++;
470            }
471            return n;
472        }
473
474        // override isEmpty() and iterator() to not call size()
475        // this avoids loading + checking all Providers
476
477        public boolean isEmpty() {
478            return (tryGet(0) == null);
479        }
480
481        public Iterator<Service> iterator() {
482            return new Iterator<Service>() {
483                int index;
484
485                public boolean hasNext() {
486                    return tryGet(index) != null;
487                }
488
489                public Service next() {
490                    Service s = tryGet(index);
491                    if (s == null) {
492                        throw new NoSuchElementException();
493                    }
494                    index++;
495                    return s;
496                }
497
498                public void remove() {
499                    throw new UnsupportedOperationException();
500                }
501            };
502        }
503    }
504
505}
506