summaryrefslogtreecommitdiffstats
path: root/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java
blob: 8a67ac201cecbc238800ca3f1d9fca98f83987ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
* @author Boris V. Kuznetsov
* @version $Revision$
*/

package org.apache.harmony.security.fortress;

import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.util.Locale;


/**
 * This class implements common functionality for Provider supplied
 * classes. The usage pattern is to allocate static Engine instance
 * per service type and synchronize on that instance during calls to
 * {@code getInstance} and retreival of the selected {@code Provider}
 * and Service Provider Interface (SPI) results. Retreiving the
 * results with {@code getProvider} and {@code getSpi} sets the
 * internal {@code Engine} values to null to prevent memory leaks.
 *
 * <p>
 *
 * For example: <pre>   {@code
 *   public class Foo {
 *
 *       private static final Engine ENGINE = new Engine("Foo");
 *
 *       private final FooSpi spi;
 *       private final Provider provider;
 *       private final String algorithm;
 *
 *       protected Foo(FooSpi spi,
 *                     Provider provider,
 *                     String algorithm) {
 *           this.spi = spi;
 *           this.provider = provider;
 *           this.algorithm = algorithm;
 *       }
 *
 *       public static Foo getInstance(String algorithm) {
 *           Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
 *           return new Foo((FooSpi) sap.spi, sap.provider, algorithm);
 *       }
 *
 *       public static Foo getInstance(String algorithm, Provider provider) {
 *           Object spi = ENGINE.getInstance(algorithm, provider, null);
 *           return new Foo((FooSpi) spi, provider, algorithm);
 *       }
 *
 *       ...
 *
 * }</pre>
 */
public class Engine {

    /**
     * Access to package visible api in java.security
     */
    public static SecurityAccess door;

    /**
     * Service name such as Cipher or SSLContext
     */
    private final String serviceName;

    /**
     * Previous result for getInstance(String, Object) optimization.
     * Only this non-Provider version of getInstance is optimized
     * since the the Provider version does not require an expensive
     * Services.getService call.
     */
    private volatile ServiceCacheEntry serviceCache;

    private static final class ServiceCacheEntry {
        /** used to test for cache hit */
        private final String algorithm;
        /** used to test for cache validity */
        private final int refreshNumber;
        /** cached result */
        private final Provider.Service service;

        private ServiceCacheEntry(String algorithm,
                                  int refreshNumber,
                                  Provider.Service service) {
            this.algorithm = algorithm;
            this.refreshNumber = refreshNumber;
            this.service = service;
        }
    }

    public static final class SpiAndProvider {
        public final Object spi;
        public final Provider provider;
        private SpiAndProvider(Object spi, Provider provider) {
            this.spi = spi;
            this.provider = provider;
        }
    }

    /**
     * Creates a Engine object
     *
     * @param service
     */
    public Engine(String service) {
        this.serviceName = service;
    }

    /**
     * Finds the appropriate service implementation and returns an
     * {@code SpiAndProvider} instance containing a reference to SPI
     * and its {@code Provider}
     */
    public SpiAndProvider getInstance(String algorithm, Object param)
            throws NoSuchAlgorithmException {
        if (algorithm == null) {
            throw new NoSuchAlgorithmException("Null algorithm name");
        }
        Services.refresh();
        Provider.Service service;
        ServiceCacheEntry cacheEntry = this.serviceCache;
        if (cacheEntry != null
                && cacheEntry.algorithm.equalsIgnoreCase(algorithm)
                && Services.refreshNumber == cacheEntry.refreshNumber) {
            service = cacheEntry.service;
        } else {
            if (Services.isEmpty()) {
                throw notFound(serviceName, algorithm);
            }
            String name = this.serviceName + "." + algorithm.toUpperCase(Locale.US);
            service = Services.getService(name);
            if (service == null) {
                throw notFound(serviceName, algorithm);
            }
            this.serviceCache = new ServiceCacheEntry(algorithm, Services.refreshNumber, service);
        }
        return new SpiAndProvider(service.newInstance(param), service.getProvider());
    }

    /**
     * Finds the appropriate service implementation and returns and
     * instance of the class that implements corresponding Service
     * Provider Interface.
     */
    public Object getInstance(String algorithm, Provider provider, Object param)
            throws NoSuchAlgorithmException {
        if (algorithm == null) {
            throw new NoSuchAlgorithmException("algorithm == null");
        }
        Provider.Service service = provider.getService(serviceName, algorithm);
        if (service == null) {
            throw notFound(serviceName, algorithm);
        }
        return service.newInstance(param);
    }

    private NoSuchAlgorithmException notFound(String serviceName, String algorithm)
            throws NoSuchAlgorithmException {
        throw new NoSuchAlgorithmException(serviceName + " " + algorithm
                                           + " implementation not found");
    }
}