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.Constructor;
020 import java.lang.reflect.InvocationTargetException;
021 import java.lang.reflect.Method;
022 import java.lang.reflect.Modifier;
023
024 import org.apache.commons.discovery.DiscoveryException;
025 import org.apache.commons.logging.Log;
026 import org.apache.commons.logging.LogFactory;
027
028 /**
029 * Various utilities to interact with {@code Class} types.
030 */
031 public class ClassUtils {
032
033 private static Log log = LogFactory.getLog(ClassUtils.class);
034
035 /**
036 * Sets the {@code Log} for this class.
037 *
038 * @param _log This class {@code Log}
039 * @deprecated This method is not thread-safe
040 */
041 @Deprecated
042 public static void setLog(Log _log) {
043 log = _log;
044 }
045
046 /**
047 * Get package name.
048 * Not all class loaders 'keep' package information,
049 * in which case Class.getPackage() returns null.
050 * This means that calling Class.getPackage().getName()
051 * is unreliable at best.
052 *
053 * @param clazz The class from which the package has to be extracted
054 * @return The string representation of the input class package
055 */
056 public static String getPackageName(Class<?> clazz) {
057 Package clazzPackage = clazz.getPackage();
058 String packageName;
059 if (clazzPackage != null) {
060 packageName = clazzPackage.getName();
061 } else {
062 String clazzName = clazz.getName();
063 packageName = clazzName.substring(0, clazzName.lastIndexOf('.'));
064 }
065 return packageName;
066 }
067
068 /**
069 * Looks for {@code public static returnType methodName(paramTypes)}.
070 *
071 * @param clazz The class where looking for the method
072 * @param returnType The method return type
073 * @param methodName The method name
074 * @param paramTypes The method arguments types
075 * @return Method {@code public static returnType methodName(paramTypes)},
076 * if found to be <strong>directly</strong> implemented by clazz.
077 */
078 public static Method findPublicStaticMethod(Class<?> clazz,
079 Class<?> returnType,
080 String methodName,
081 Class<?>[] paramTypes) {
082 boolean problem = false;
083 Method method = null;
084
085 // verify '<methodName>(<paramTypes>)' is directly in class.
086 try {
087 method = clazz.getDeclaredMethod(methodName, paramTypes);
088 } catch(NoSuchMethodException e) {
089 problem = true;
090 log.debug("Class " + clazz.getName() + ": missing method '" + methodName + "(...)", e);
091 }
092
093 // verify 'public static <returnType>'
094 if (!problem &&
095 !(Modifier.isPublic(method.getModifiers()) &&
096 Modifier.isStatic(method.getModifiers()) &&
097 method.getReturnType() == returnType)) {
098 if (log.isDebugEnabled()) {
099 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 }