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 }