001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    
020    import java.lang.reflect.Array;
021    import java.lang.reflect.InvocationTargetException;
022    import java.lang.reflect.Method;
023    
024    import java.util.ArrayList;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Map;
028    
029    /**
030     * @author Brian Wing Shun Chan
031     * @author Harry Mark
032     * @author Shuyang Zhou
033     */
034    public class MethodInvoker {
035    
036            public static Object invoke(MethodWrapper methodWrapper)
037                    throws ClassNotFoundException, IllegalAccessException,
038                               InstantiationException, InvocationTargetException,
039                               NoSuchFieldException, NoSuchMethodException {
040    
041                    return invoke(methodWrapper, true);
042            }
043    
044            public static Object invoke(
045                            MethodWrapper methodWrapper, boolean newInstance)
046                    throws ClassNotFoundException, IllegalAccessException,
047                               InstantiationException, InvocationTargetException,
048                               NoSuchFieldException, NoSuchMethodException {
049    
050                    Object targetObject = null;
051    
052                    if (newInstance) {
053                            Thread currentThread = Thread.currentThread();
054    
055                            ClassLoader contextClassLoader =
056                                    currentThread.getContextClassLoader();
057    
058                            targetObject = contextClassLoader.loadClass(
059                                    methodWrapper.getClassName()).newInstance();
060                    }
061    
062                    Object[] methodAndArguments = _lookupMethodAndArguments(
063                            methodWrapper, targetObject);
064    
065                    Object returnObject = null;
066    
067                    if (methodAndArguments[0] != null) {
068                            Method method = (Method)methodAndArguments[0];
069                            Object[] arguments = (Object[])methodAndArguments[1];
070    
071                            returnObject = method.invoke(targetObject, arguments);
072                    }
073    
074                    return returnObject;
075            }
076    
077            public static Object invoke(
078                            MethodWrapper methodWrapper, Object targetObject)
079                    throws ClassNotFoundException, IllegalAccessException,
080                               InvocationTargetException, NoSuchFieldException,
081                               NoSuchMethodException {
082    
083                    Object[] methodAndArguments = _lookupMethodAndArguments(
084                            methodWrapper, targetObject);
085    
086                    Object returnObject = null;
087    
088                    if (methodAndArguments[0] != null) {
089                            Method method = (Method)methodAndArguments[0];
090                            Object[] arguments = (Object[])methodAndArguments[1];
091    
092                            returnObject = method.invoke(targetObject, arguments);
093                    }
094    
095                    return returnObject;
096            }
097    
098            private static Object[] _lookupMethodAndArguments(
099                            MethodWrapper methodWrapper, Object targetObject)
100                    throws ClassNotFoundException, IllegalAccessException,
101                               InvocationTargetException, NoSuchFieldException,
102                               NoSuchMethodException {
103    
104                    Object[] methodAndArguments = new Object[2];
105    
106                    Thread currentThread = Thread.currentThread();
107    
108                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
109    
110                    String className = methodWrapper.getClassName();
111                    String methodName = methodWrapper.getMethodName();
112                    Object[] arguments = methodWrapper.getArguments();
113                    String[] argumentClassNames = methodWrapper.getArgumentClassNames();
114    
115                    List<Class<?>> parameterTypes = new ArrayList<Class<?>>();
116    
117                    for (int i = 0; i < arguments.length; i++) {
118                            if (arguments[i] == null) {
119                                    _log.error(
120                                            "Cannot invoke " + className + " " + methodName +
121                                                    " on position " + i + " because it is null");
122                            }
123    
124                            Class<?> argClass = null;
125    
126                            if (argumentClassNames != null) {
127                                    argClass = _primitiveTypeMap.get(argumentClassNames[i]);
128    
129                                    if (argClass == null) {
130                                            argClass = Class.forName(
131                                                    argumentClassNames[i], true, contextClassLoader);
132                                    }
133                            }
134                            else {
135                                    argClass = arguments[i].getClass();
136                            }
137    
138                            if (ClassUtil.isSubclass(argClass, PrimitiveWrapper.class)) {
139                                    parameterTypes.add(
140                                            (Class<?>)argClass.getField("TYPE").get(arguments[i]));
141    
142                                    MethodKey methodKey = new MethodKey(
143                                            argClass.getName(), "getValue", null);
144    
145                                    Method method = MethodCache.get(methodKey);
146    
147                                    arguments[i] = method.invoke(arguments[i], (Object[])null);
148                            }
149                            else if (arguments[i] instanceof NullWrapper) {
150                                    NullWrapper nullWrapper = (NullWrapper)arguments[i];
151    
152                                    String wrappedClassName = nullWrapper.getClassName();
153    
154                                    if (wrappedClassName.startsWith(StringPool.OPEN_BRACKET) &&
155                                            wrappedClassName.endsWith(StringPool.SEMICOLON)) {
156    
157                                            wrappedClassName = wrappedClassName.substring(
158                                                    2, wrappedClassName.length() - 1);
159    
160                                            Class<?> wrappedClass = contextClassLoader.loadClass(
161                                                    wrappedClassName);
162    
163                                            parameterTypes.add(
164                                                    Array.newInstance(wrappedClass, 0).getClass());
165                                    }
166                                    else {
167                                            Class<?> wrappedClass = contextClassLoader.loadClass(
168                                                    wrappedClassName);
169    
170                                            parameterTypes.add(wrappedClass);
171                                    }
172    
173                                    arguments[i] = null;
174                            }
175                            else {
176                                    parameterTypes.add(argClass);
177                            }
178                    }
179    
180                    MethodKey methodKey = null;
181    
182                    Method method = null;
183    
184                    try {
185                            methodKey = new MethodKey(
186                                    methodWrapper.getClassName(), methodWrapper.getMethodName(),
187                                    parameterTypes.toArray(new Class[parameterTypes.size()]));
188    
189                            method = MethodCache.get(methodKey);
190                    }
191                    catch (NoSuchMethodException nsme) {
192                            Class<?> classObject = null;
193    
194                            if (targetObject == null) {
195                                    classObject = contextClassLoader.loadClass(className);
196                            }
197                            else {
198                                    classObject = targetObject.getClass();
199                            }
200    
201                            Method[] methods = classObject.getMethods();
202    
203                            for (int i = 0; i < methods.length; i++) {
204                                    Class<?>[] methodParameterTypes =
205                                            methods[i].getParameterTypes();
206    
207                                    if (methods[i].getName().equals(methodName) &&
208                                            methodParameterTypes.length == parameterTypes.size()) {
209    
210                                            boolean correctParams = true;
211    
212                                            for (int j = 0; j < parameterTypes.size(); j++) {
213                                                    Class<?> a = parameterTypes.get(j);
214                                                    Class<?> b = methodParameterTypes[j];
215    
216                                                    if (!ClassUtil.isSubclass(a, b)) {
217                                                            correctParams = false;
218    
219                                                            break;
220                                                    }
221                                            }
222    
223                                            if (correctParams) {
224                                                    method = methods[i];
225    
226                                                    MethodCache.put(methodKey, method);
227    
228                                                    break;
229                                            }
230                                    }
231                            }
232    
233                            if (method == null) {
234                                    throw nsme;
235                            }
236                    }
237    
238                    methodAndArguments[0] = method;
239                    methodAndArguments[1] = arguments;
240    
241                    return methodAndArguments;
242            }
243    
244            private static Log _log = LogFactoryUtil.getLog(MethodInvoker.class);
245    
246            private static Map<String, Class<?>> _primitiveTypeMap =
247                    new HashMap<String, Class<?>>();
248    
249            static {
250                    _primitiveTypeMap.put("char", char.class);
251                    _primitiveTypeMap.put("boolean", boolean.class);
252                    _primitiveTypeMap.put("byte", byte.class);
253                    _primitiveTypeMap.put("double", double.class);
254                    _primitiveTypeMap.put("float", float.class);
255                    _primitiveTypeMap.put("int", int.class);
256                    _primitiveTypeMap.put("long", long.class);
257                    _primitiveTypeMap.put("short", short.class);
258            }
259    
260    }