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 }