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    }