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 package org.apache.commons.discovery.tools;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23
24 import org.apache.commons.discovery.DiscoveryException;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 /**
29 * Various utilities to interact with {@code Class} types.
30 */
31 public class ClassUtils {
32
33 private static Log log = LogFactory.getLog(ClassUtils.class);
34
35 /**
36 * Sets the {@code Log} for this class.
37 *
38 * @param _log This class {@code Log}
39 * @deprecated This method is not thread-safe
40 */
41 @Deprecated
42 public static void setLog(Log _log) {
43 log = _log;
44 }
45
46 /**
47 * Get package name.
48 * Not all class loaders 'keep' package information,
49 * in which case Class.getPackage() returns null.
50 * This means that calling Class.getPackage().getName()
51 * is unreliable at best.
52 *
53 * @param clazz The class from which the package has to be extracted
54 * @return The string representation of the input class package
55 */
56 public static String getPackageName(Class<?> clazz) {
57 Package clazzPackage = clazz.getPackage();
58 String packageName;
59 if (clazzPackage != null) {
60 packageName = clazzPackage.getName();
61 } else {
62 String clazzName = clazz.getName();
63 packageName = clazzName.substring(0, clazzName.lastIndexOf('.'));
64 }
65 return packageName;
66 }
67
68 /**
69 * Looks for {@code public static returnType methodName(paramTypes)}.
70 *
71 * @param clazz The class where looking for the method
72 * @param returnType The method return type
73 * @param methodName The method name
74 * @param paramTypes The method arguments types
75 * @return Method {@code public static returnType methodName(paramTypes)},
76 * if found to be <strong>directly</strong> implemented by clazz.
77 */
78 public static Method findPublicStaticMethod(Class<?> clazz,
79 Class<?> returnType,
80 String methodName,
81 Class<?>[] paramTypes) {
82 boolean problem = false;
83 Method method = null;
84
85 // verify '<methodName>(<paramTypes>)' is directly in class.
86 try {
87 method = clazz.getDeclaredMethod(methodName, paramTypes);
88 } catch(NoSuchMethodException e) {
89 problem = true;
90 log.debug("Class " + clazz.getName() + ": missing method '" + methodName + "(...)", e);
91 }
92
93 // verify 'public static <returnType>'
94 if (!problem &&
95 !(Modifier.isPublic(method.getModifiers()) &&
96 Modifier.isStatic(method.getModifiers()) &&
97 method.getReturnType() == returnType)) {
98 if (log.isDebugEnabled()) {
99 if (!Modifier.isPublic(method.getModifiers())) {
100 log.debug(methodName + "() is not public");
101 }
102 if (!Modifier.isStatic(method.getModifiers())) {
103 log.debug(methodName + "() is not static");
104 }
105 if (method.getReturnType() != returnType) {
106 log.debug("Method returns: "
107 + method.getReturnType().getName()
108 + "@@"
109 + method.getReturnType().getClassLoader());
110 log.debug("Should return: "
111 + returnType.getName()
112 + "@@"
113 + returnType.getClassLoader());
114 }
115 }
116 problem = true;
117 method = null;
118 }
119
120 return method;
121 }
122
123 /**
124 * Creates a new instance of the input class using the following policy:
125 *
126 * <ul>
127 * <li>if <code>paramClasses</code> or <code>params</code> is null,
128 * the default constructor will be used;</li>
129 * <li>the public constructor with <code>paramClasses</code> arguments type,
130 * with <code>params</code> as arguments value, will be used.</li>
131 * </ul>
132 *
133 * @param <T> The class type has to be instantiated
134 * @param impl The class has to be instantiated
135 * @param paramClasses The constructor arguments types (can be {@code null})
136 * @param params The constructor arguments values (can be {@code null})
137 * @return A new class instance
138 * @throws DiscoveryException if the class implementing
139 * the SPI cannot be found, cannot be loaded and
140 * instantiated, or if the resulting class does not implement
141 * (or extend) the SPI
142 * @throws InstantiationException see {@link Class#newInstance()}
143 * @throws IllegalAccessException see {@link Class#newInstance()}
144 * @throws NoSuchMethodException see {@link Class#newInstance()}
145 * @throws InvocationTargetException see {@link Class#newInstance()}
146 */
147 public static <T> T newInstance(Class<T> impl, Class<?> paramClasses[], Object params[]) throws DiscoveryException,
148 InstantiationException,
149 IllegalAccessException,
150 NoSuchMethodException,
151 InvocationTargetException {
152 if (paramClasses == null || params == null) {
153 return impl.newInstance();
154 }
155
156 Constructor<T> constructor = impl.getConstructor(paramClasses);
157 return constructor.newInstance(params);
158 }
159
160 /**
161 * Throws exception if {@code impl} does not
162 * implement or extend the SPI.
163 *
164 * @param spi The SPI type
165 * @param impl The class has to be verified is a SPI implementation/extension
166 * @throws DiscoveryException if the input implementation class is not an SPI implementation
167 */
168 public static void verifyAncestory(Class<?> spi, Class<?> impl) throws DiscoveryException {
169 if (spi == null) {
170 throw new DiscoveryException("No interface defined!");
171 }
172
173 if (impl == null) {
174 throw new DiscoveryException("No implementation defined for " + spi.getName());
175 }
176
177 if (!spi.isAssignableFrom(impl)) {
178 throw new DiscoveryException("Class "
179 + impl.getName()
180 + " does not implement "
181 + spi.getName());
182 }
183 }
184
185 }