Home | History | Annotate | Download | only in fortress
      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);
     80                 providersNames.put(p.getName(), p);
     81                 initServiceInfo(p);
     82             } catch (ClassNotFoundException ignored) {
     83             } catch (IllegalAccessException ignored) {
     84             } catch (InstantiationException ignored) {
     85             }
     86         }
     87         Engine.door.renumProviders();
     88     }
     89 
     90     /**
     91      * Returns a copy of the registered providers as an array.
     92      */
     93     public static synchronized ArrayList<Provider> getProviders() {
     94         return providers;
     95     }
     96 
     97     /**
     98      * Returns the provider with the specified name.
     99      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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