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      * Save default SecureRandom service as well.
     34      * Avoids similar provider/services iteration in SecureRandom constructor.
     35      */
     36     private static Provider.Service cachedSecureRandomService;
     37 
     38     /**
     39      * Need refresh flag.
     40      */
     41     private static boolean needRefresh;
     42 
     43     /**
     44      * The cacheVersion is changed on every update of service
     45      * information. It is used by external callers to validate their
     46      * own caches of Service information.
     47      */
     48     private static int cacheVersion = 1;
     49 
     50     /**
     51      * Registered providers.
     52      */
     53     private static final ArrayList<Provider> providers = new ArrayList<Provider>(20);
     54 
     55     /**
     56      * Try to load and register a provider by name from the given class-loader.
     57      */
     58     private static boolean initProvider(String providerClassName, ClassLoader classLoader) {
     59         try {
     60             Class<?> providerClass = Class.forName(providerClassName.trim(), true, classLoader);
     61             Provider p = (Provider) providerClass.newInstance();
     62             providers.add(p);
     63             providersNames.put(p.getName(), p);
     64             return true;
     65         } catch (ClassNotFoundException ignored) {
     66         } catch (IllegalAccessException ignored) {
     67         } catch (InstantiationException ignored) {
     68         }
     69         return false;
     70     }
     71 
     72     /**
     73      * Hash for quick provider access by name.
     74      */
     75     private static final HashMap<String, Provider> providersNames
     76             = new HashMap<String, Provider>(20);
     77     static {
     78         String providerClassName = null;
     79         int i = 1;
     80         ClassLoader cl = Services.class.getClassLoader();
     81 
     82         while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
     83             if (!initProvider(providerClassName, cl)) {
     84                 // Not on the boot classpath. Try the system class-loader.
     85                 // Note: DO NOT USE A LOCAL FOR GETSYSTEMCLASSLOADER! This will break compile-time
     86                 //       initialization.
     87                 if (!initProvider(providerClassName, ClassLoader.getSystemClassLoader())) {
     88                     // TODO: Logging?
     89                 }
     90             }
     91         }
     92         Engine.door.renumProviders();
     93         setNeedRefresh();
     94     }
     95 
     96     /**
     97      * Returns the actual registered providers.
     98      */
     99     public static synchronized ArrayList<Provider> getProviders() {
    100         return providers;
    101     }
    102 
    103     /**
    104      * Returns the provider with the specified name.
    105      */
    106     public static synchronized Provider getProvider(String name) {
    107         if (name == null) {
    108             return null;
    109         }
    110         return providersNames.get(name);
    111     }
    112 
    113     /**
    114      * Inserts a provider at a specified 1-based position.
    115      */
    116     public static synchronized int insertProviderAt(Provider provider, int position) {
    117         int size = providers.size();
    118         if ((position < 1) || (position > size)) {
    119             position = size + 1;
    120         }
    121         providers.add(position - 1, provider);
    122         providersNames.put(provider.getName(), provider);
    123         setNeedRefresh();
    124         return position;
    125     }
    126 
    127     /**
    128      * Removes the provider at the specified 1-based position.
    129      */
    130     public static synchronized void removeProvider(int providerNumber) {
    131         Provider p = providers.remove(providerNumber - 1);
    132         providersNames.remove(p.getName());
    133         setNeedRefresh();
    134     }
    135 
    136     /**
    137      * Looks up the requested service by type and algorithm. The service
    138      * {@code type} and should be provided in the same format used when
    139      * registering a service with a provider, for example, "KeyFactory.RSA".
    140      * Callers can cache the returned service information but such caches should
    141      * be validated against the result of Service.getCacheVersion() before use.
    142      * Returns {@code null} if there are no services found.
    143      */
    144     public static synchronized ArrayList<Provider.Service> getServices(String type,
    145             String algorithm) {
    146         ArrayList<Provider.Service> services = null;
    147         for (Provider p : providers) {
    148             Provider.Service s = p.getService(type, algorithm);
    149             if (s != null) {
    150                 if (services == null) {
    151                     services = new ArrayList<>(providers.size());
    152                 }
    153                 services.add(s);
    154             }
    155         }
    156         return services;
    157     }
    158 
    159     /**
    160      * Finds the first service offered of {@code type} and returns it.
    161      */
    162     private static synchronized Provider.Service getFirstServiceOfType(String type) {
    163         for (Provider p : providers) {
    164             Provider.Service s = Engine.door.getService(p, type);
    165             if (s != null) {
    166                 return s;
    167             }
    168         }
    169         return null;
    170     }
    171 
    172     /**
    173      * Returns the default SecureRandom service description.
    174      */
    175     public static synchronized Provider.Service getSecureRandomService() {
    176         getCacheVersion();  // used for side effect of updating cache if needed
    177         return cachedSecureRandomService;
    178     }
    179 
    180     /**
    181      * In addition to being used here when the list of providers
    182      * changes, this method is also used by the Provider
    183      * implementation to indicate that a provides list of services has
    184      * changed.
    185      */
    186     public static synchronized void setNeedRefresh() {
    187         needRefresh = true;
    188     }
    189 
    190     /**
    191      * Returns the current cache version. This has the possible side
    192      * effect of updating the cache if needed.
    193      */
    194     public static synchronized int getCacheVersion() {
    195         if (needRefresh) {
    196             cacheVersion++;
    197             cachedSecureRandomService = getFirstServiceOfType("SecureRandom");
    198             needRefresh = false;
    199         }
    200         return cacheVersion;
    201     }
    202 }
    203