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 /**
     19 * @author Boris V. Kuznetsov
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.security.fortress;
     24 
     25 import java.security.NoSuchAlgorithmException;
     26 import java.security.Provider;
     27 import java.util.Locale;
     28 
     29 
     30 /**
     31  * This class implements common functionality for Provider supplied
     32  * classes. The usage pattern is to allocate static Engine instance
     33  * per service type and synchronize on that instance during calls to
     34  * {@code getInstance} and retreival of the selected {@code Provider}
     35  * and Service Provider Interface (SPI) results. Retreiving the
     36  * results with {@code getProvider} and {@code getSpi} sets the
     37  * internal {@code Engine} values to null to prevent memory leaks.
     38  *
     39  * <p>
     40  *
     41  * For example: <pre>   {@code
     42  *   public class Foo {
     43  *
     44  *       private static final Engine ENGINE = new Engine("Foo");
     45  *
     46  *       private final FooSpi spi;
     47  *       private final Provider provider;
     48  *       private final String algorithm;
     49  *
     50  *       protected Foo(FooSpi spi,
     51  *                     Provider provider,
     52  *                     String algorithm) {
     53  *           this.spi = spi;
     54  *           this.provider = provider;
     55  *           this.algorithm = algorithm;
     56  *       }
     57  *
     58  *       public static Foo getInstance(String algorithm) {
     59  *           Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
     60  *           return new Foo((FooSpi) sap.spi, sap.provider, algorithm);
     61  *       }
     62  *
     63  *       public static Foo getInstance(String algorithm, Provider provider) {
     64  *           Object spi = ENGINE.getInstance(algorithm, provider, null);
     65  *           return new Foo((FooSpi) spi, provider, algorithm);
     66  *       }
     67  *
     68  *       ...
     69  *
     70  * }</pre>
     71  */
     72 public class Engine {
     73 
     74     /**
     75      * Access to package visible api in java.security
     76      */
     77     public static SecurityAccess door;
     78 
     79     /**
     80      * Service name such as Cipher or SSLContext
     81      */
     82     private final String serviceName;
     83 
     84     /**
     85      * Previous result for getInstance(String, Object) optimization.
     86      * Only this non-Provider version of getInstance is optimized
     87      * since the the Provider version does not require an expensive
     88      * Services.getService call.
     89      */
     90     private volatile ServiceCacheEntry serviceCache;
     91 
     92     private static final class ServiceCacheEntry {
     93         /** used to test for cache hit */
     94         private final String algorithm;
     95         /** used to test for cache validity */
     96         private final int refreshNumber;
     97         /** cached result */
     98         private final Provider.Service service;
     99 
    100         private ServiceCacheEntry(String algorithm,
    101                                   int refreshNumber,
    102                                   Provider.Service service) {
    103             this.algorithm = algorithm;
    104             this.refreshNumber = refreshNumber;
    105             this.service = service;
    106         }
    107     }
    108 
    109     public static final class SpiAndProvider {
    110         public final Object spi;
    111         public final Provider provider;
    112         private SpiAndProvider(Object spi, Provider provider) {
    113             this.spi = spi;
    114             this.provider = provider;
    115         }
    116     }
    117 
    118     /**
    119      * Creates a Engine object
    120      *
    121      * @param service
    122      */
    123     public Engine(String service) {
    124         this.serviceName = service;
    125     }
    126 
    127     /**
    128      * Finds the appropriate service implementation and returns an
    129      * {@code SpiAndProvider} instance containing a reference to SPI
    130      * and its {@code Provider}
    131      */
    132     public SpiAndProvider getInstance(String algorithm, Object param)
    133             throws NoSuchAlgorithmException {
    134         if (algorithm == null) {
    135             throw new NoSuchAlgorithmException("Null algorithm name");
    136         }
    137         Services.refresh();
    138         Provider.Service service;
    139         ServiceCacheEntry cacheEntry = this.serviceCache;
    140         if (cacheEntry != null
    141                 && cacheEntry.algorithm.equalsIgnoreCase(algorithm)
    142                 && Services.refreshNumber == cacheEntry.refreshNumber) {
    143             service = cacheEntry.service;
    144         } else {
    145             if (Services.isEmpty()) {
    146                 throw notFound(serviceName, algorithm);
    147             }
    148             String name = this.serviceName + "." + algorithm.toUpperCase(Locale.US);
    149             service = Services.getService(name);
    150             if (service == null) {
    151                 throw notFound(serviceName, algorithm);
    152             }
    153             this.serviceCache = new ServiceCacheEntry(algorithm, Services.refreshNumber, service);
    154         }
    155         return new SpiAndProvider(service.newInstance(param), service.getProvider());
    156     }
    157 
    158     /**
    159      * Finds the appropriate service implementation and returns and
    160      * instance of the class that implements corresponding Service
    161      * Provider Interface.
    162      */
    163     public Object getInstance(String algorithm, Provider provider, Object param)
    164             throws NoSuchAlgorithmException {
    165         if (algorithm == null) {
    166             throw new NoSuchAlgorithmException("algorithm == null");
    167         }
    168         Provider.Service service = provider.getService(serviceName, algorithm);
    169         if (service == null) {
    170             throw notFound(serviceName, algorithm);
    171         }
    172         return service.newInstance(param);
    173     }
    174 
    175     private NoSuchAlgorithmException notFound(String serviceName, String algorithm)
    176             throws NoSuchAlgorithmException {
    177         throw new NoSuchAlgorithmException(serviceName + " " + algorithm
    178                                            + " implementation not found");
    179     }
    180 }
    181