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 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; 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 */ 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 */ 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 */ 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 */ 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 */ 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 188 private NoSuchAlgorithmException notFound(String serviceName, String algorithm) 189 throws NoSuchAlgorithmException { 190 throw new NoSuchAlgorithmException(serviceName + " " + algorithm 191 + " implementation not found"); 192 } 193 } 194