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