• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ArrayList;
28 import java.util.Locale;
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 retrieval of the selected {@code Provider}
35  * and Service Provider Interface (SPI) results. Retrieving 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 final 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 cacheVersion;
97         /** cached result */
98         private final ArrayList<Provider.Service> services;
99 
ServiceCacheEntry(String algorithm, int cacheVersion, ArrayList<Provider.Service> services)100         private ServiceCacheEntry(String algorithm,
101                                   int cacheVersion,
102                                   ArrayList<Provider.Service> services) {
103             this.algorithm = algorithm;
104             this.cacheVersion = cacheVersion;
105             this.services = services;
106         }
107     }
108 
109     public static final class SpiAndProvider {
110         public final Object spi;
111         public final Provider provider;
SpiAndProvider(Object spi, 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 serviceName
122      */
Engine(String serviceName)123     public Engine(String serviceName) {
124         this.serviceName = serviceName;
125     }
126 
127     /**
128      * Finds the appropriate service implementation and returns an
129      * {@code SpiAndProvider} instance containing a reference to the first
130      * matching SPI and its {@code Provider}
131      */
getInstance(String algorithm, Object param)132     public SpiAndProvider getInstance(String algorithm, Object param)
133             throws NoSuchAlgorithmException {
134         if (algorithm == null) {
135             throw new NoSuchAlgorithmException("Null algorithm name");
136         }
137         ArrayList<Provider.Service> services = getServices(algorithm);
138         if (services == null) {
139             throw notFound(this.serviceName, algorithm);
140         }
141         return new SpiAndProvider(services.get(0).newInstance(param), services.get(0).getProvider());
142     }
143 
144     /**
145      * Finds the appropriate service implementation and returns an
146      * {@code SpiAndProvider} instance containing a reference to SPI
147      * and its {@code Provider}
148      */
getInstance(Provider.Service service, String param)149     public SpiAndProvider getInstance(Provider.Service service, String param)
150             throws NoSuchAlgorithmException {
151         return new SpiAndProvider(service.newInstance(param), service.getProvider());
152     }
153 
154     /**
155      * Returns a list of all possible matches for a given algorithm.
156      */
getServices(String algorithm)157     public ArrayList<Provider.Service> getServices(String algorithm) {
158         int newCacheVersion = Services.getCacheVersion();
159         ServiceCacheEntry cacheEntry = this.serviceCache;
160         final String algoUC = algorithm.toUpperCase(Locale.US);
161         if (cacheEntry != null
162                 && cacheEntry.algorithm.equalsIgnoreCase(algoUC)
163                 && newCacheVersion == cacheEntry.cacheVersion) {
164             return cacheEntry.services;
165         }
166         String name = this.serviceName + "." + algoUC;
167         ArrayList<Provider.Service> services = Services.getServices(name);
168         this.serviceCache = new ServiceCacheEntry(algoUC, newCacheVersion, services);
169         return services;
170     }
171 
172     /**
173      * Finds the appropriate service implementation and returns and instance of
174      * the class that implements corresponding Service Provider Interface.
175      */
getInstance(String algorithm, Provider provider, Object param)176     public Object getInstance(String algorithm, Provider provider, Object param)
177             throws NoSuchAlgorithmException {
178         if (algorithm == null) {
179             throw new NoSuchAlgorithmException("algorithm == null");
180         }
181         Provider.Service service = provider.getService(serviceName, algorithm);
182         if (service == null) {
183             throw notFound(serviceName, algorithm);
184         }
185         return service.newInstance(param);
186     }
187 
notFound(String serviceName, String algorithm)188     private NoSuchAlgorithmException notFound(String serviceName, String algorithm)
189             throws NoSuchAlgorithmException {
190         throw new NoSuchAlgorithmException(serviceName + " " + algorithm
191                                            + " implementation not found");
192     }
193 }
194