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    }