1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package org.apache.harmony.security.fortress;
19 
20 import java.security.Provider;
21 import java.security.Security;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Locale;
25 
26 
27 /**
28  * This class contains information about all registered providers and preferred
29  * implementations for all "serviceName.algName".
30  */
31 public class Services {
32 
33     /**
34      * The HashMap that contains information about preferred implementations for
35      * all serviceName.algName in the registered providers.
36      * Set the initial size to 600 so we don't grow to 1024 by default because
37      * initialization adds a few entries more than the growth threshold.
38      */
39     private static final HashMap<String, ArrayList<Provider.Service>> services
40             = new HashMap<String, ArrayList<Provider.Service>>(600);
41 
42     /**
43      * Save default SecureRandom service as well.
44      * Avoids similar provider/services iteration in SecureRandom constructor.
45      */
46     private static Provider.Service cachedSecureRandomService;
47 
48     /**
49      * Need refresh flag.
50      */
51     private static boolean needRefresh;
52 
53     /**
54      * The cacheVersion is changed on every update of service
55      * information. It is used by external callers to validate their
56      * own caches of Service information.
57      */
58     private static int cacheVersion = 1;
59 
60     /**
61      * Registered providers.
62      */
63     private static final ArrayList<Provider> providers = new ArrayList<Provider>(20);
64 
65     /**
66      * Hash for quick provider access by name.
67      */
68     private static final HashMap<String, Provider> providersNames
69             = new HashMap<String, Provider>(20);
70     static {
71         String providerClassName = null;
72         int i = 1;
73         ClassLoader cl = ClassLoader.getSystemClassLoader();
74 
75         while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
76             try {
77                 Class<?> providerClass = Class.forName(providerClassName.trim(), true, cl);
78                 Provider p = (Provider) providerClass.newInstance();
79                 providers.add(p);
p.getName()80                 providersNames.put(p.getName(), p);
81                 initServiceInfo(p);
82             } catch (ClassNotFoundException ignored) {
83             } catch (IllegalAccessException ignored) {
84             } catch (InstantiationException ignored) {
85             }
86         }
Engine.door.renumProviders()87         Engine.door.renumProviders();
88     }
89 
90     /**
91      * Returns a copy of the registered providers as an array.
92      */
getProviders()93     public static synchronized ArrayList<Provider> getProviders() {
94         return providers;
95     }
96 
97     /**
98      * Returns the provider with the specified name.
99      */
getProvider(String name)100     public static synchronized Provider getProvider(String name) {
101         if (name == null) {
102             return null;
103         }
104         return providersNames.get(name);
105     }
106 
107     /**
108      * Inserts a provider at a specified 1-based position.
109      */
insertProviderAt(Provider provider, int position)110     public static synchronized int insertProviderAt(Provider provider, int position) {
111         int size = providers.size();
112         if ((position < 1) || (position > size)) {
113             position = size + 1;
114         }
115         providers.add(position - 1, provider);
116         providersNames.put(provider.getName(), provider);
117         setNeedRefresh();
118         return position;
119     }
120 
121     /**
122      * Removes the provider at the specified 1-based position.
123      */
removeProvider(int providerNumber)124     public static synchronized void removeProvider(int providerNumber) {
125         Provider p = providers.remove(providerNumber - 1);
126         providersNames.remove(p.getName());
127         setNeedRefresh();
128     }
129 
130     /**
131      * Adds information about provider services into HashMap.
132      */
initServiceInfo(Provider p)133     public static synchronized void initServiceInfo(Provider p) {
134         for (Provider.Service service : p.getServices()) {
135             String type = service.getType();
136             if (cachedSecureRandomService == null && type.equals("SecureRandom")) {
137                 cachedSecureRandomService = service;
138             }
139             String key = type + "." + service.getAlgorithm().toUpperCase(Locale.US);
140             appendServiceLocked(key, service);
141             for (String alias : Engine.door.getAliases(service)) {
142                 key = type + "." + alias.toUpperCase(Locale.US);
143                 appendServiceLocked(key, service);
144             }
145         }
146     }
147 
148     /**
149      * Add or append the service to the key.
150      */
appendServiceLocked(String key, Provider.Service service)151     private static void appendServiceLocked(String key, Provider.Service service) {
152         ArrayList<Provider.Service> serviceList = services.get(key);
153         if (serviceList == null) {
154             serviceList = new ArrayList<Provider.Service>(1);
155             services.put(key, serviceList);
156         }
157         serviceList.add(service);
158     }
159 
160     /**
161      * Returns true if services does not contain any provider information.
162      */
isEmpty()163     public static synchronized boolean isEmpty() {
164         return services.isEmpty();
165     }
166 
167     /**
168      * Looks up the requested service by type and algorithm. The
169      * service key should be provided in the same format used when
170      * registering a service with a provider, for example,
171      * "KeyFactory.RSA".
172      *
173      * Callers can cache the returned service information but such
174      * caches should be validated against the result of
175      * Service.getCacheVersion() before use.
176      */
getServices(String key)177     public static synchronized ArrayList<Provider.Service> getServices(String key) {
178         return services.get(key);
179     }
180 
181     /**
182      * Returns the default SecureRandom service description.
183      */
getSecureRandomService()184     public static synchronized Provider.Service getSecureRandomService() {
185         getCacheVersion();  // used for side effect of updating cache if needed
186         return cachedSecureRandomService;
187     }
188 
189     /**
190      * In addition to being used here when the list of providers
191      * changes, this method is also used by the Provider
192      * implementation to indicate that a provides list of services has
193      * changed.
194      */
setNeedRefresh()195     public static synchronized void setNeedRefresh() {
196         needRefresh = true;
197     }
198 
199     /**
200      * Returns the current cache version. This has the possible side
201      * effect of updating the cache if needed.
202      */
getCacheVersion()203     public static synchronized int getCacheVersion() {
204         if (needRefresh) {
205             cacheVersion++;
206             synchronized (services) {
207                 services.clear();
208             }
209             cachedSecureRandomService = null;
210             for (Provider p : providers) {
211                 initServiceInfo(p);
212             }
213             needRefresh = false;
214         }
215         return cacheVersion;
216     }
217 }
218