1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import com.liferay.portal.kernel.log.Log;
18  import com.liferay.portal.kernel.log.LogFactoryUtil;
19  
20  import java.lang.reflect.Array;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  /**
30   * <a href="MethodInvoker.java.html"><b><i>View Source</i></b></a>
31   *
32   * @author Brian Wing Shun Chan
33   * @author Harry Mark
34   * @author Shuyang Zhou
35   */
36  public class MethodInvoker {
37  
38      public static Object invoke(MethodWrapper methodWrapper)
39          throws ClassNotFoundException, IllegalAccessException,
40                 InstantiationException, InvocationTargetException,
41                 NoSuchFieldException, NoSuchMethodException {
42  
43          return invoke(methodWrapper, true);
44      }
45  
46      public static Object invoke(
47              MethodWrapper methodWrapper, boolean newInstance)
48          throws ClassNotFoundException, IllegalAccessException,
49                 InstantiationException, InvocationTargetException,
50                 NoSuchFieldException, NoSuchMethodException {
51  
52          Object targetObject = null;
53  
54          if (newInstance) {
55              Thread currentThread = Thread.currentThread();
56  
57              ClassLoader contextClassLoader =
58                  currentThread.getContextClassLoader();
59  
60              targetObject = contextClassLoader.loadClass(
61                  methodWrapper.getClassName()).newInstance();
62          }
63  
64          Object[] methodAndArguments = _lookupMethodAndArguments(
65              methodWrapper, targetObject);
66  
67          Object returnObject = null;
68  
69          if (methodAndArguments[0] != null) {
70              Method method = (Method)methodAndArguments[0];
71              Object[] arguments = (Object[])methodAndArguments[1];
72  
73              returnObject = method.invoke(targetObject, arguments);
74          }
75  
76          return returnObject;
77      }
78  
79      public static Object invoke(
80              MethodWrapper methodWrapper, Object targetObject)
81          throws ClassNotFoundException, IllegalAccessException,
82                 InvocationTargetException, NoSuchFieldException,
83                 NoSuchMethodException {
84  
85          Object[] methodAndArguments = _lookupMethodAndArguments(
86              methodWrapper, targetObject);
87  
88          Object returnObject = null;
89  
90          if (methodAndArguments[0] != null) {
91              Method method = (Method)methodAndArguments[0];
92              Object[] arguments = (Object[])methodAndArguments[1];
93  
94              returnObject = method.invoke(targetObject, arguments);
95          }
96  
97          return returnObject;
98      }
99  
100     private static Object[] _lookupMethodAndArguments(
101             MethodWrapper methodWrapper, Object targetObject)
102         throws ClassNotFoundException, IllegalAccessException,
103                InvocationTargetException, NoSuchFieldException,
104                NoSuchMethodException {
105 
106         Object[] methodAndArguments = new Object[2];
107 
108         Thread currentThread = Thread.currentThread();
109 
110         ClassLoader contextClassLoader = currentThread.getContextClassLoader();
111 
112         String className = methodWrapper.getClassName();
113         String methodName = methodWrapper.getMethodName();
114         Object[] arguments = methodWrapper.getArguments();
115         String[] argumentClassNames = methodWrapper.getArgumentClassNames();
116 
117         List<Class<?>> parameterTypes = new ArrayList<Class<?>>();
118 
119         for (int i = 0; i < arguments.length; i++) {
120             if (arguments[i] == null) {
121                 _log.error(
122                     "Cannot invoke " + className + " " + methodName +
123                         " on position " + i + " because it is null");
124             }
125 
126             Class<?> argClass = null;
127 
128             if (argumentClassNames != null) {
129                 argClass = _primitiveTypeMap.get(argumentClassNames[i]);
130 
131                 if (argClass == null) {
132                     argClass = Class.forName(
133                         argumentClassNames[i], true, contextClassLoader);
134                 }
135             }
136             else {
137                 argClass = arguments[i].getClass();
138             }
139 
140             if (ClassUtil.isSubclass(argClass, PrimitiveWrapper.class)) {
141                 parameterTypes.add(
142                     (Class<?>)argClass.getField("TYPE").get(arguments[i]));
143 
144                 MethodKey methodKey = new MethodKey(
145                     argClass.getName(), "getValue", null);
146 
147                 Method method = MethodCache.get(methodKey);
148 
149                 arguments[i] = method.invoke(arguments[i], (Object[])null);
150             }
151             else if (arguments[i] instanceof NullWrapper) {
152                 NullWrapper nullWrapper = (NullWrapper)arguments[i];
153 
154                 String wrappedClassName = nullWrapper.getClassName();
155 
156                 if (wrappedClassName.startsWith(StringPool.OPEN_BRACKET) &&
157                     wrappedClassName.endsWith(StringPool.SEMICOLON)) {
158 
159                     wrappedClassName = wrappedClassName.substring(
160                         2, wrappedClassName.length() - 1);
161 
162                     Class<?> wrappedClass = contextClassLoader.loadClass(
163                         wrappedClassName);
164 
165                     parameterTypes.add(
166                         Array.newInstance(wrappedClass, 0).getClass());
167                 }
168                 else {
169                     Class<?> wrappedClass = contextClassLoader.loadClass(
170                         wrappedClassName);
171 
172                     parameterTypes.add(wrappedClass);
173                 }
174 
175                 arguments[i] = null;
176             }
177             else {
178                 parameterTypes.add(argClass);
179             }
180         }
181 
182         MethodKey methodKey = null;
183 
184         Method method = null;
185 
186         try {
187             methodKey = new MethodKey(
188                 methodWrapper.getClassName(), methodWrapper.getMethodName(),
189                 parameterTypes.toArray(new Class[parameterTypes.size()]));
190 
191             method = MethodCache.get(methodKey);
192         }
193         catch (NoSuchMethodException nsme) {
194             Class<?> classObject = null;
195 
196             if (targetObject == null) {
197                 classObject = contextClassLoader.loadClass(className);
198             }
199             else {
200                 classObject = targetObject.getClass();
201             }
202 
203             Method[] methods = classObject.getMethods();
204 
205             for (int i = 0; i < methods.length; i++) {
206                 Class<?>[] methodParameterTypes =
207                     methods[i].getParameterTypes();
208 
209                 if (methods[i].getName().equals(methodName) &&
210                     methodParameterTypes.length == parameterTypes.size()) {
211 
212                     boolean correctParams = true;
213 
214                     for (int j = 0; j < parameterTypes.size(); j++) {
215                         Class<?> a = parameterTypes.get(j);
216                         Class<?> b = methodParameterTypes[j];
217 
218                         if (!ClassUtil.isSubclass(a, b)) {
219                             correctParams = false;
220 
221                             break;
222                         }
223                     }
224 
225                     if (correctParams) {
226                         method = methods[i];
227 
228                         MethodCache.put(methodKey, method);
229 
230                         break;
231                     }
232                 }
233             }
234 
235             if (method == null) {
236                 throw nsme;
237             }
238         }
239 
240         methodAndArguments[0] = method;
241         methodAndArguments[1] = arguments;
242 
243         return methodAndArguments;
244     }
245 
246     private static Log _log = LogFactoryUtil.getLog(MethodInvoker.class);
247 
248     private static Map<String, Class<?>> _primitiveTypeMap =
249         new HashMap<String, Class<?>>();
250 
251     static {
252         _primitiveTypeMap.put("char", char.class);
253         _primitiveTypeMap.put("boolean", boolean.class);
254         _primitiveTypeMap.put("byte", byte.class);
255         _primitiveTypeMap.put("double", double.class);
256         _primitiveTypeMap.put("float", float.class);
257         _primitiveTypeMap.put("int", int.class);
258         _primitiveTypeMap.put("long", long.class);
259         _primitiveTypeMap.put("short", short.class);
260     }
261 
262 }