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
23package org.apache.harmony.security.fortress;
24
25import java.security.NoSuchAlgorithmException;
26import java.security.Provider;
27import 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 */
72public 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 Provider.Service service;
99
100        private ServiceCacheEntry(String algorithm,
101                                  int cacheVersion,
102                                  Provider.Service service) {
103            this.algorithm = algorithm;
104            this.cacheVersion = cacheVersion;
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        int newCacheVersion = Services.getCacheVersion();
138        Provider.Service service;
139        ServiceCacheEntry cacheEntry = this.serviceCache;
140        if (cacheEntry != null
141                && cacheEntry.algorithm.equalsIgnoreCase(algorithm)
142                && newCacheVersion == cacheEntry.cacheVersion) {
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, newCacheVersion, 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