001 package org.apache.ojb.broker.util;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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
018 import java.lang.reflect.InvocationTargetException;
019 import java.lang.reflect.Method;
020 import java.lang.reflect.Field;
021 import java.lang.reflect.Constructor;
022 import java.lang.reflect.Modifier;
023 import java.net.URL;
024
025 import org.apache.ojb.broker.OJBRuntimeException;
026 import org.apache.ojb.broker.PersistenceBrokerException;
027 import org.apache.ojb.broker.metadata.ClassDescriptor;
028 import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
029
030 /**
031 * Helper class with static methods for java class, method, and field handling.
032 *
033 * @version $Id: ClassHelper.java,v 1.1 2007-08-24 22:17:36 ewestfal Exp $
034 */
035 public class ClassHelper
036 {
037 /** Arguments for invoking a default or no-arg constructor */
038 private static final Object[] NO_ARGS = {};
039 /** Parameter types of a default/no-arg constructor */
040 private static final Class[] NO_ARGS_CLASS = {};
041
042 /** The class loader currently used by OJB */
043 private static ClassLoader _classLoader = null;
044 /** A mutex for changing the class loader */
045 private static Object _mutex = new Object();
046
047 /**
048 * Prevents instatiation.
049 */
050 private ClassHelper()
051 {
052 }
053
054 /**
055 * Sets the classloader to be used by OJB. This can be set by external
056 * application that need to pass a specific classloader to OJB.
057 *
058 * @param loader The class loader. If <code>null</code> then OJB will use
059 * the class loader of the current thread
060 */
061 public static void setClassLoader(ClassLoader loader)
062 {
063 synchronized (_mutex)
064 {
065 _classLoader = loader;
066 }
067 }
068
069 /**
070 * Returns the class loader currently used by OJB. Defaults to the class loader of
071 * the current thread (<code>Thread.currentThread().getContextClassLoader()</code>)
072 * if not set differently. If class loader is not explicitly set and the loader for
073 * the current thread context is null, the JVM default class loader will be used.
074 *
075 * @return The classloader used by OJB
076 * @see #setClassLoader(ClassLoader)
077 */
078 public static ClassLoader getClassLoader()
079 {
080 final ClassLoader ojbClassLoader;
081 if (_classLoader != null)
082 {
083 ojbClassLoader = _classLoader;
084 }
085 else
086 {
087 final ClassLoader threadCtxtClassLoader;
088 threadCtxtClassLoader = Thread.currentThread().getContextClassLoader();
089 if (threadCtxtClassLoader == null)
090 {
091 // mkalen: happens only in "obscure" situations using JNI, revert to system CL
092 ojbClassLoader = ClassLoader.getSystemClassLoader();
093 }
094 else
095 {
096 ojbClassLoader = threadCtxtClassLoader;
097 }
098 }
099 return ojbClassLoader;
100 }
101
102 /**
103 * Determines the url of the indicated resource using the currently set class loader.
104 *
105 * @param name The resource name
106 * @return The resource's url
107 */
108 public static URL getResource(String name)
109 {
110 return getClassLoader().getResource(name);
111 }
112
113 /**
114 * Retrieves the class object for the given qualified class name.
115 *
116 * @param className The qualified name of the class
117 * @param initialize Whether the class shall be initialized
118 * @return The class object
119 */
120 public static Class getClass(String className, boolean initialize) throws ClassNotFoundException
121 {
122 return Class.forName(className, initialize, getClassLoader());
123 }
124
125 /**
126 * Returns a new instance of the given class, using the default or a no-arg constructor.
127 *
128 * @param target The class to instantiate
129 * @return The instance
130 */
131 public static Object newInstance(Class target) throws InstantiationException,
132 IllegalAccessException
133 {
134 return target.newInstance();
135 }
136
137 /**
138 * Returns a new instance of the given class, using the default or a no-arg constructor.
139 * This method can also use private no-arg constructors if <code>makeAccessible</code>
140 * is set to <code>true</code> (and there are no other security constraints).
141 *
142 * @param target The class to instantiate
143 * @param makeAccessible If the constructor shall be made accessible prior to using it
144 * @return The instance
145 */
146 public static Object newInstance(Class target, boolean makeAccessible) throws InstantiationException,
147 IllegalAccessException
148 {
149 if (makeAccessible)
150 {
151 try
152 {
153 return newInstance(target, NO_ARGS_CLASS, NO_ARGS, makeAccessible);
154 }
155 catch (InvocationTargetException e)
156 {
157 throw new OJBRuntimeException("Unexpected exception while instantiate class '"
158 + target + "' with default constructor", e);
159 }
160 catch (NoSuchMethodException e)
161 {
162 throw new OJBRuntimeException("Unexpected exception while instantiate class '"
163 + target + "' with default constructor", e);
164 }
165 }
166 else
167 {
168 return target.newInstance();
169 }
170 }
171
172 /**
173 * Returns a new instance of the given class, using the constructor with the specified parameter types.
174 *
175 * @param target The class to instantiate
176 * @param types The parameter types
177 * @param args The arguments
178 * @return The instance
179 */
180 public static Object newInstance(Class target, Class[] types, Object[] args) throws InstantiationException,
181 IllegalAccessException,
182 IllegalArgumentException,
183 InvocationTargetException,
184 NoSuchMethodException,
185 SecurityException
186 {
187 return newInstance(target, types, args, false);
188 }
189
190 /**
191 * Returns a new instance of the given class, using the constructor with the specified parameter types.
192 * This method can also use private constructors if <code>makeAccessible</code> is set to
193 * <code>true</code> (and there are no other security constraints).
194 *
195 * @param target The class to instantiate
196 * @param types The parameter types
197 * @param args The arguments
198 * @param makeAccessible If the constructor shall be made accessible prior to using it
199 * @return The instance
200 */
201 public static Object newInstance(Class target, Class[] types, Object[] args, boolean makeAccessible) throws InstantiationException,
202 IllegalAccessException,
203 IllegalArgumentException,
204 InvocationTargetException,
205 NoSuchMethodException,
206 SecurityException
207 {
208 Constructor con;
209
210 if (makeAccessible)
211 {
212 con = target.getDeclaredConstructor(types);
213 if (makeAccessible && !con.isAccessible())
214 {
215 con.setAccessible(true);
216 }
217 }
218 else
219 {
220 con = target.getConstructor(types);
221 }
222 return con.newInstance(args);
223 }
224
225 /**
226 * Determines the method with the specified signature via reflection look-up.
227 *
228 * @param clazz The java class to search in
229 * @param methodName The method's name
230 * @param params The parameter types
231 * @return The method object or <code>null</code> if no matching method was found
232 */
233 public static Method getMethod(Class clazz, String methodName, Class[] params)
234 {
235 try
236 {
237 return clazz.getMethod(methodName, params);
238 }
239 catch (Exception ignored)
240 {}
241 return null;
242 }
243
244 /**
245 * Determines the field via reflection look-up.
246 *
247 * @param clazz The java class to search in
248 * @param fieldName The field's name
249 * @return The field object or <code>null</code> if no matching field was found
250 */
251 public static Field getField(Class clazz, String fieldName)
252 {
253 try
254 {
255 return clazz.getField(fieldName);
256 }
257 catch (Exception ignored)
258 {}
259 return null;
260 }
261
262
263 // *******************************************************************
264 // Convenience methods
265 // *******************************************************************
266
267 /**
268 * Convenience method for {@link #getClass(String, boolean) getClass(name, true)}
269 *
270 * @param name The qualified class name
271 * @return The class object
272 */
273 public static Class getClass(String name) throws ClassNotFoundException
274 {
275 return getClass(name, true);
276 }
277
278
279 /**
280 * Returns a new instance of the class with the given qualified name using the default or
281 * or a no-arg constructor.
282 *
283 * @param className The qualified name of the class to instantiate
284 */
285 public static Object newInstance(String className) throws InstantiationException,
286 IllegalAccessException,
287 ClassNotFoundException
288 {
289 return newInstance(getClass(className));
290 }
291
292 /**
293 * Returns a new instance of the class with the given qualified name using the constructor with
294 * the specified signature.
295 *
296 * @param className The qualified name of the class to instantiate
297 * @param types The parameter types
298 * @param args The arguments
299 * @return The instance
300 */
301 public static Object newInstance(String className, Class[] types, Object[] args) throws InstantiationException,
302 IllegalAccessException,
303 IllegalArgumentException,
304 InvocationTargetException,
305 NoSuchMethodException,
306 SecurityException,
307 ClassNotFoundException
308 {
309 return newInstance(getClass(className), types, args);
310 }
311
312 /**
313 * Returns a new instance of the given class using the constructor with the specified parameter.
314 *
315 * @param target The class to instantiate
316 * @param type The types of the single parameter of the constructor
317 * @param arg The argument
318 * @return The instance
319 */
320 public static Object newInstance(Class target, Class type, Object arg) throws InstantiationException,
321 IllegalAccessException,
322 IllegalArgumentException,
323 InvocationTargetException,
324 NoSuchMethodException,
325 SecurityException
326 {
327 return newInstance(target, new Class[]{ type }, new Object[]{ arg });
328 }
329
330 /**
331 * Returns a new instance of the class with the given qualified name using the constructor with
332 * the specified parameter.
333 *
334 * @param className The qualified name of the class to instantiate
335 * @param type The types of the single parameter of the constructor
336 * @param arg The argument
337 * @return The instance
338 */
339 public static Object newInstance(String className, Class type, Object arg) throws InstantiationException,
340 IllegalAccessException,
341 IllegalArgumentException,
342 InvocationTargetException,
343 NoSuchMethodException,
344 SecurityException,
345 ClassNotFoundException
346 {
347 return newInstance(className, new Class[]{type}, new Object[]{arg});
348 }
349
350 /**
351 * Determines the method with the specified signature via reflection look-up.
352 *
353 * @param object The instance whose class is searched for the method
354 * @param methodName The method's name
355 * @param params The parameter types
356 * @return A method object or <code>null</code> if no matching method was found
357 */
358 public static Method getMethod(Object object, String methodName, Class[] params)
359 {
360 return getMethod(object.getClass(), methodName, params);
361 }
362
363 /**
364 * Determines the method with the specified signature via reflection look-up.
365 *
366 * @param className The qualified name of the searched class
367 * @param methodName The method's name
368 * @param params The parameter types
369 * @return A method object or <code>null</code> if no matching method was found
370 */
371 public static Method getMethod(String className, String methodName, Class[] params)
372 {
373 try
374 {
375 return getMethod(getClass(className, false), methodName, params);
376 }
377 catch (Exception ignored)
378 {}
379 return null;
380 }
381
382 /**
383 * Builds a new instance for the class represented by the given class descriptor.
384 *
385 * @param cld The class descriptor
386 * @return The instance
387 */
388 public static Object buildNewObjectInstance(ClassDescriptor cld)
389 {
390 Object result = null;
391
392 // If either the factory class and/or factory method is null,
393 // just follow the normal code path and create via constructor
394 if ((cld.getFactoryClass() == null) || (cld.getFactoryMethod() == null))
395 {
396 try
397 {
398 // 1. create an empty Object (persistent classes need a public default constructor)
399 Constructor con = cld.getZeroArgumentConstructor();
400 if(con == null)
401 {
402 throw new ClassNotPersistenceCapableException(
403 "A zero argument constructor was not provided! Class was '" + cld.getClassNameOfObject() + "'");
404 }
405 result = ConstructorHelper.instantiate(con);
406 }
407 catch (InstantiationException e)
408 {
409 throw new ClassNotPersistenceCapableException(
410 "Can't instantiate class '" + cld.getClassNameOfObject()+"'");
411 }
412 }
413 else
414 {
415 try
416 {
417 // 1. create an empty Object by calling the no-parms factory method
418 Method method = cld.getFactoryMethod();
419
420 if (Modifier.isStatic(method.getModifiers()))
421 {
422 // method is static so call it directly
423 result = method.invoke(null, null);
424 }
425 else
426 {
427 // method is not static, so create an object of the factory first
428 // note that this requires a public no-parameter (default) constructor
429 Object factoryInstance = cld.getFactoryClass().newInstance();
430
431 result = method.invoke(factoryInstance, null);
432 }
433 }
434 catch (Exception ex)
435 {
436 throw new PersistenceBrokerException("Unable to build object instance of class '"
437 + cld.getClassNameOfObject() + "' from factory:" + cld.getFactoryClass()
438 + "." + cld.getFactoryMethod(), ex);
439 }
440 }
441 return result;
442 }
443 }