001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.discovery.tools; 018 019 import java.lang.reflect.InvocationTargetException; 020 021 import org.apache.commons.discovery.DiscoveryException; 022 023 /** 024 * Represents a Service Programming Interface (spi). 025 * - SPI's name 026 * - SPI's (provider) class 027 * - SPI's (alternate) override property name 028 * 029 * In addition, while there are many cases where this is NOT 030 * usefull, for those in which it is: 031 * 032 * - expected constructor argument types and parameters values. 033 * 034 * @param <T> The SPI type 035 */ 036 public class SPInterface<T> { 037 038 /** 039 * Construct object representing Class {@code provider}. 040 * 041 * @param <T> The SPI type 042 * @param provider The SPI class 043 * @return A new object representing Class {@code provider} 044 * @since 0.5 045 */ 046 public static <T> SPInterface<T> newSPInterface(Class<T> provider) { 047 return newSPInterface(provider, provider.getName()); 048 } 049 050 /** 051 * Construct object representing Class {@code provider}. 052 * 053 * @param <T> The SPI type 054 * @param provider The SPI class 055 * @param propertyName when looking for the name of a class implementing 056 * the provider class, a discovery strategy may involve looking for 057 * (system or other) properties having either the name of the class 058 * (provider) or the <code>propertyName</code>. 059 * @return A new object representing Class {@code provider} 060 * @since 0.5 061 */ 062 public static <T> SPInterface<T> newSPInterface(Class<T> provider, String propertyName) { 063 return new SPInterface<T>(provider, propertyName); 064 } 065 066 /** 067 * Construct object representing Class {@code provider}. 068 * 069 * @param <T> The SPI type 070 * @param provider The SPI class 071 * @param constructorParamClasses classes representing the 072 * constructor argument types 073 * @param constructorParams objects representing the 074 * constructor arguments 075 * @return A new object representing Class {@code provider} 076 * @since 0.5 077 */ 078 public static <T> SPInterface<T> newSPInterface(Class<T> provider, 079 Class<?> constructorParamClasses[], 080 Object constructorParams[]) { 081 return newSPInterface(provider, provider.getName(), constructorParamClasses, constructorParams); 082 } 083 084 /** 085 * Construct object representing Class {@code provider}. 086 * 087 * @param <T> The SPI type 088 * @param provider The SPI class 089 * @param propertyName when looking for the name of a class implementing 090 * the provider class, a discovery strategy may involve looking for 091 * (system or other) properties having either the name of the class 092 * (provider) or the <code>propertyName</code>. 093 * @param constructorParamClasses classes representing the 094 * constructor argument types 095 * @param constructorParams objects representing the 096 * constructor arguments 097 * @return A new object representing Class {@code provider} 098 * @since 0.5 099 */ 100 public static <T> SPInterface<T> newSPInterface(Class<T> provider, 101 String propertyName, 102 Class<?> constructorParamClasses[], 103 Object constructorParams[]) { 104 return new SPInterface<T>(provider, propertyName, constructorParamClasses, constructorParams); 105 } 106 107 /** 108 * The service programming interface: intended to be 109 * an interface or abstract class, but not limited 110 * to those two. 111 */ 112 private final Class<T> spi; 113 114 /** 115 * The property name to be used for finding the name of 116 * the SPI implementation class. 117 */ 118 private final String propertyName; 119 120 private final Class<?> paramClasses[]; 121 122 private final Object params[]; 123 124 /** 125 * Construct object representing Class <code>provider</code>. 126 * 127 * @param provider The SPI class 128 */ 129 public SPInterface(Class<T> provider) { 130 this(provider, provider.getName()); 131 } 132 133 /** 134 * Construct object representing Class <code>provider</code>. 135 * 136 * @param spi The SPI class 137 * 138 * @param propertyName when looking for the name of a class implementing 139 * the provider class, a discovery strategy may involve looking for 140 * (system or other) properties having either the name of the class 141 * (provider) or the <code>propertyName</code>. 142 */ 143 public SPInterface(Class<T> spi, String propertyName) { 144 this.spi = spi; 145 this.propertyName = propertyName; 146 this.paramClasses = null; 147 this.params = null; 148 } 149 150 /** 151 * Construct object representing Class <code>provider</code>. 152 * 153 * @param provider The SPI class 154 * 155 * @param constructorParamClasses classes representing the 156 * constructor argument types. 157 * 158 * @param constructorParams objects representing the 159 * constructor arguments. 160 */ 161 public SPInterface(Class<T> provider, 162 Class<?> constructorParamClasses[], 163 Object constructorParams[]) { 164 this(provider, 165 provider.getName(), 166 constructorParamClasses, 167 constructorParams); 168 } 169 170 /** 171 * Construct object representing Class <code>provider</code>. 172 * 173 * @param spi The SPI class 174 * 175 * @param propertyName when looking for the name of a class implementing 176 * the provider class, a discovery strategy may involve looking for 177 * (system or other) properties having either the name of the class 178 * (provider) or the <code>propertyName</code>. 179 * 180 * @param constructorParamClasses classes representing the 181 * constructor argument types. 182 * 183 * @param constructorParams objects representing the 184 * constructor arguments. 185 */ 186 public SPInterface(Class<T> spi, 187 String propertyName, 188 Class<?> constructorParamClasses[], 189 Object constructorParams[]) { 190 this.spi = spi; 191 this.propertyName = propertyName; 192 this.paramClasses = constructorParamClasses; 193 this.params = constructorParams; 194 } 195 196 /** 197 * Returns the SPI class name. 198 * 199 * @return The SPI class name 200 */ 201 public String getSPName() { 202 return spi.getName(); 203 } 204 205 /** 206 * Returns the SPI class. 207 * 208 * @return The SPI class 209 */ 210 public Class<T> getSPClass() { 211 return spi; 212 } 213 214 /** 215 * Returns the property name to be used for finding 216 * the name of the SPI implementation class. 217 * 218 * @return The property name to be used for finding 219 * the name of the SPI implementation class 220 */ 221 public String getPropertyName() { 222 return propertyName; 223 } 224 225 /** 226 * Creates a new instance of the given SPI class. 227 * 228 * @param <S> Any type extends T 229 * @param impl The SPI class has to be instantiated 230 * @return A new instance of the given SPI class 231 * @throws DiscoveryException if the class implementing 232 * the SPI cannot be found, cannot be loaded and 233 * instantiated, or if the resulting class does not implement 234 * (or extend) the SPI 235 * @throws InstantiationException see {@link Class#newInstance()} 236 * @throws IllegalAccessException see {@link Class#newInstance()} 237 * @throws NoSuchMethodException see {@link Class#newInstance()} 238 * @throws InvocationTargetException see {@link Class#newInstance()} 239 */ 240 public <S extends T> S newInstance(Class<S> impl) 241 throws DiscoveryException, 242 InstantiationException, 243 IllegalAccessException, 244 NoSuchMethodException, 245 InvocationTargetException { 246 verifyAncestory(impl); 247 248 return ClassUtils.newInstance(impl, paramClasses, params); 249 } 250 251 /** 252 * Verifies the given SPI implementation is a SPI specialization. 253 * 254 * @param <S> Any type extends T 255 * @param impl The SPI instantance 256 */ 257 public <S extends T> void verifyAncestory(Class<S> impl) { 258 ClassUtils.verifyAncestory(spi, impl); 259 } 260 261 }