View Javadoc

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 }