001
014
015 package com.liferay.portal.action;
016
017 import com.liferay.portal.kernel.json.JSONArray;
018 import com.liferay.portal.kernel.json.JSONException;
019 import com.liferay.portal.kernel.json.JSONFactoryUtil;
020 import com.liferay.portal.kernel.json.JSONObject;
021 import com.liferay.portal.kernel.json.JSONSerializable;
022 import com.liferay.portal.kernel.json.JSONSerializer;
023 import com.liferay.portal.kernel.log.Log;
024 import com.liferay.portal.kernel.log.LogFactoryUtil;
025 import com.liferay.portal.kernel.util.ArrayUtil;
026 import com.liferay.portal.kernel.util.GetterUtil;
027 import com.liferay.portal.kernel.util.LocalizationUtil;
028 import com.liferay.portal.kernel.util.ParamUtil;
029 import com.liferay.portal.kernel.util.SetUtil;
030 import com.liferay.portal.kernel.util.StringPool;
031 import com.liferay.portal.kernel.util.StringUtil;
032 import com.liferay.portal.kernel.util.Validator;
033 import com.liferay.portal.service.ServiceContext;
034 import com.liferay.portal.service.ServiceContextUtil;
035 import com.liferay.portal.struts.JSONAction;
036 import com.liferay.portal.util.PropsValues;
037
038 import java.lang.reflect.Method;
039 import java.lang.reflect.Type;
040
041 import java.util.Arrays;
042 import java.util.Date;
043 import java.util.HashMap;
044 import java.util.Map;
045 import java.util.Set;
046 import java.util.regex.Matcher;
047 import java.util.regex.Pattern;
048
049 import javax.servlet.http.HttpServletRequest;
050 import javax.servlet.http.HttpServletResponse;
051
052 import org.apache.struts.action.ActionForm;
053 import org.apache.struts.action.ActionMapping;
054
055
061 public class JSONServiceAction extends JSONAction {
062
063 public JSONServiceAction() {
064 _invalidClassNames = SetUtil.fromArray(
065 PropsValues.JSON_SERVICE_INVALID_CLASS_NAMES);
066
067 if (_log.isDebugEnabled()) {
068 for (String invalidClassName : _invalidClassNames) {
069 _log.debug("Invalid class name " + invalidClassName);
070 }
071 }
072 }
073
074 @Override
075 public String getJSON(
076 ActionMapping actionMapping, ActionForm actionForm,
077 HttpServletRequest request, HttpServletResponse response)
078 throws Exception {
079
080 String className = ParamUtil.getString(request, "serviceClassName");
081 String methodName = ParamUtil.getString(request, "serviceMethodName");
082 String[] serviceParameters = getStringArrayFromJSON(
083 request, "serviceParameters");
084 String[] serviceParameterTypes = getStringArrayFromJSON(
085 request, "serviceParameterTypes");
086
087 if (!isValidRequest(request)) {
088 return null;
089 }
090
091 Thread currentThread = Thread.currentThread();
092
093 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
094
095 Class<?> clazz = contextClassLoader.loadClass(className);
096
097 Object[] methodAndParameterTypes = getMethodAndParameterTypes(
098 clazz, methodName, serviceParameters, serviceParameterTypes);
099
100 if (methodAndParameterTypes != null) {
101 Method method = (Method)methodAndParameterTypes[0];
102 Type[] parameterTypes = (Type[])methodAndParameterTypes[1];
103 Object[] args = new Object[serviceParameters.length];
104
105 for (int i = 0; i < serviceParameters.length; i++) {
106 args[i] = getArgValue(
107 request, clazz, methodName, serviceParameters[i],
108 parameterTypes[i]);
109 }
110
111 try {
112 if (_log.isDebugEnabled()) {
113 _log.debug(
114 "Invoking " + clazz + " on method " + method.getName() +
115 " with args " + Arrays.toString(args));
116 }
117
118 Object returnObj = method.invoke(clazz, args);
119
120 if (returnObj != null) {
121 return getReturnValue(returnObj);
122 }
123 else {
124 return JSONFactoryUtil.getNullJSON();
125 }
126 }
127 catch (Exception e) {
128 if (_log.isDebugEnabled()) {
129 _log.debug(
130 "Invoked " + clazz + " on method " + method.getName() +
131 " with args " + Arrays.toString(args),
132 e);
133 }
134
135 return JSONFactoryUtil.serializeException(e);
136 }
137 }
138
139 return null;
140 }
141
142 protected Object getArgValue(
143 HttpServletRequest request, Class<?> clazz, String methodName,
144 String parameter, Type parameterType)
145 throws Exception {
146
147 String typeNameOrClassDescriptor = getTypeNameOrClassDescriptor(
148 parameterType);
149
150 String value = ParamUtil.getString(request, parameter);
151
152 if (Validator.isNull(value) &&
153 !typeNameOrClassDescriptor.equals("[Ljava.lang.String;")) {
154
155 return null;
156 }
157 else if (typeNameOrClassDescriptor.equals("boolean") ||
158 typeNameOrClassDescriptor.equals(Boolean.class.getName())) {
159
160 return Boolean.valueOf(ParamUtil.getBoolean(request, parameter));
161 }
162 else if (typeNameOrClassDescriptor.equals("double") ||
163 typeNameOrClassDescriptor.equals(Double.class.getName())) {
164
165 return new Double(ParamUtil.getDouble(request, parameter));
166 }
167 else if (typeNameOrClassDescriptor.equals("int") ||
168 typeNameOrClassDescriptor.equals(Integer.class.getName())) {
169
170 return new Integer(ParamUtil.getInteger(request, parameter));
171 }
172 else if (typeNameOrClassDescriptor.equals("long") ||
173 typeNameOrClassDescriptor.equals(Long.class.getName())) {
174
175 return new Long(ParamUtil.getLong(request, parameter));
176 }
177 else if (typeNameOrClassDescriptor.equals("short") ||
178 typeNameOrClassDescriptor.equals(Short.class.getName())) {
179
180 return new Short(ParamUtil.getShort(request, parameter));
181 }
182 else if (typeNameOrClassDescriptor.equals(Date.class.getName())) {
183 return new Date(ParamUtil.getLong(request, parameter));
184 }
185 else if (typeNameOrClassDescriptor.equals(
186 ServiceContext.class.getName())) {
187
188 JSONObject jsonObject = JSONFactoryUtil.createJSONObject(value);
189
190 jsonObject.put("javaClass", ServiceContext.class.getName());
191
192 return ServiceContextUtil.deserialize(jsonObject);
193 }
194 else if (typeNameOrClassDescriptor.equals(String.class.getName())) {
195 return value;
196 }
197 else if (typeNameOrClassDescriptor.equals("[Z")) {
198 return ParamUtil.getBooleanValues(request, parameter);
199 }
200 else if (typeNameOrClassDescriptor.equals("[D")) {
201 return ParamUtil.getDoubleValues(request, parameter);
202 }
203 else if (typeNameOrClassDescriptor.equals("[F")) {
204 return ParamUtil.getFloatValues(request, parameter);
205 }
206 else if (typeNameOrClassDescriptor.equals("[I")) {
207 return ParamUtil.getIntegerValues(request, parameter);
208 }
209 else if (typeNameOrClassDescriptor.equals("[J")) {
210 return ParamUtil.getLongValues(request, parameter);
211 }
212 else if (typeNameOrClassDescriptor.equals("[S")) {
213 return ParamUtil.getShortValues(request, parameter);
214 }
215 else if (typeNameOrClassDescriptor.equals("[Ljava.lang.String;")) {
216 return ParamUtil.getParameterValues(request, parameter);
217 }
218 else if (typeNameOrClassDescriptor.equals("[[Z")) {
219 String[] values = request.getParameterValues(parameter);
220
221 if ((values != null) && (values.length > 0)) {
222 String[] values0 = StringUtil.split(values[0]);
223
224 boolean[][] doubleArray =
225 new boolean[values.length][values0.length];
226
227 for (int i = 0; i < values.length; i++) {
228 String[] curValues = StringUtil.split(values[i]);
229
230 for (int j = 0; j < curValues.length; j++) {
231 doubleArray[i][j] = GetterUtil.getBoolean(curValues[j]);
232 }
233 }
234
235 return doubleArray;
236 }
237 else {
238 return new boolean[0][0];
239 }
240 }
241 else if (typeNameOrClassDescriptor.equals("[[D")) {
242 String[] values = request.getParameterValues(parameter);
243
244 if ((values != null) && (values.length > 0)) {
245 String[] values0 = StringUtil.split(values[0]);
246
247 double[][] doubleArray =
248 new double[values.length][values0.length];
249
250 for (int i = 0; i < values.length; i++) {
251 String[] curValues = StringUtil.split(values[i]);
252
253 for (int j = 0; j < curValues.length; j++) {
254 doubleArray[i][j] = GetterUtil.getDouble(curValues[j]);
255 }
256 }
257
258 return doubleArray;
259 }
260 else {
261 return new double[0][0];
262 }
263 }
264 else if (typeNameOrClassDescriptor.equals("[[F")) {
265 String[] values = request.getParameterValues(parameter);
266
267 if ((values != null) && (values.length > 0)) {
268 String[] values0 = StringUtil.split(values[0]);
269
270 float[][] doubleArray =
271 new float[values.length][values0.length];
272
273 for (int i = 0; i < values.length; i++) {
274 String[] curValues = StringUtil.split(values[i]);
275
276 for (int j = 0; j < curValues.length; j++) {
277 doubleArray[i][j] = GetterUtil.getFloat(curValues[j]);
278 }
279 }
280
281 return doubleArray;
282 }
283 else {
284 return new float[0][0];
285 }
286 }
287 else if (typeNameOrClassDescriptor.equals("[[I")) {
288 String[] values = request.getParameterValues(parameter);
289
290 if ((values != null) && (values.length > 0)) {
291 String[] values0 = StringUtil.split(values[0]);
292
293 int[][] doubleArray =
294 new int[values.length][values0.length];
295
296 for (int i = 0; i < values.length; i++) {
297 String[] curValues = StringUtil.split(values[i]);
298
299 for (int j = 0; j < curValues.length; j++) {
300 doubleArray[i][j] = GetterUtil.getInteger(curValues[j]);
301 }
302 }
303
304 return doubleArray;
305 }
306 else {
307 return new int[0][0];
308 }
309 }
310 else if (typeNameOrClassDescriptor.equals("[[J")) {
311 String[] values = request.getParameterValues(parameter);
312
313 if ((values != null) && (values.length > 0)) {
314 String[] values0 = StringUtil.split(values[0]);
315
316 long[][] doubleArray =
317 new long[values.length][values0.length];
318
319 for (int i = 0; i < values.length; i++) {
320 String[] curValues = StringUtil.split(values[i]);
321
322 for (int j = 0; j < curValues.length; j++) {
323 doubleArray[i][j] = GetterUtil.getLong(curValues[j]);
324 }
325 }
326
327 return doubleArray;
328 }
329 else {
330 return new long[0][0];
331 }
332 }
333 else if (typeNameOrClassDescriptor.equals("[[S")) {
334 String[] values = request.getParameterValues(parameter);
335
336 if ((values != null) && (values.length > 0)) {
337 String[] values0 = StringUtil.split(values[0]);
338
339 short[][] doubleArray =
340 new short[values.length][values0.length];
341
342 for (int i = 0; i < values.length; i++) {
343 String[] curValues = StringUtil.split(values[i]);
344
345 for (int j = 0; j < curValues.length; j++) {
346 doubleArray[i][j] = GetterUtil.getShort(curValues[j]);
347 }
348 }
349
350 return doubleArray;
351 }
352 else {
353 return new short[0][0];
354 }
355 }
356 else if (typeNameOrClassDescriptor.equals("[[Ljava.lang.String")) {
357 String[] values = request.getParameterValues(parameter);
358
359 if ((values != null) && (values.length > 0)) {
360 String[] values0 = StringUtil.split(values[0]);
361
362 String[][] doubleArray =
363 new String[values.length][values0.length];
364
365 for (int i = 0; i < values.length; i++) {
366 doubleArray[i] = StringUtil.split(values[i]);
367 }
368
369 return doubleArray;
370 }
371 else {
372 return new String[0][0];
373 }
374 }
375 else if (typeNameOrClassDescriptor.equals(
376 "java.util.Map<java.util.Locale, java.lang.String>")) {
377
378 JSONObject jsonObject = JSONFactoryUtil.createJSONObject(value);
379
380 return LocalizationUtil.deserialize(jsonObject);
381 }
382 else {
383 try {
384 return JSONFactoryUtil.looseDeserialize(value);
385 }
386 catch (Exception e) {
387 }
388
389 _log.error(
390 "Unsupported parameter type for class " + clazz + ", method " +
391 methodName + ", parameter " + parameter + ", and type " +
392 typeNameOrClassDescriptor);
393
394 return null;
395 }
396 }
397
398 protected Object[] getMethodAndParameterTypes(
399 Class<?> clazz, String methodName, String[] parameters,
400 String[] parameterTypes)
401 throws Exception {
402
403 String parameterNames = StringUtil.merge(parameters);
404
405 String key =
406 clazz.getName() + "_METHOD_NAME_" + methodName + "_PARAMETERS_" +
407 parameterNames;
408
409 Object[] methodAndParameterTypes = _methodCache.get(key);
410
411 if (methodAndParameterTypes != null) {
412 return methodAndParameterTypes;
413 }
414
415 Method method = null;
416 Type[] methodParameterTypes = null;
417
418 Method[] methods = clazz.getMethods();
419
420 for (Method curMethod : methods) {
421 if (curMethod.getName().equals(methodName)) {
422 Type[] curParameterTypes = curMethod.getGenericParameterTypes();
423
424 if (curParameterTypes.length == parameters.length) {
425 if ((parameterTypes.length > 0) &&
426 (parameterTypes.length == curParameterTypes.length)) {
427
428 boolean match = true;
429
430 for (int j = 0; j < parameterTypes.length; j++) {
431 String t1 = parameterTypes[j];
432 String t2 = getTypeNameOrClassDescriptor(
433 curParameterTypes[j]);
434
435 if (!t1.equals(t2)) {
436 match = false;
437 }
438 }
439
440 if (match) {
441 method = curMethod;
442 methodParameterTypes = curParameterTypes;
443
444 break;
445 }
446 }
447 else if (method != null) {
448 _log.error(
449 "Obscure method name for class " + clazz +
450 ", method " + methodName + ", and parameters " +
451 parameterNames);
452
453 return null;
454 }
455 else {
456 method = curMethod;
457 methodParameterTypes = curParameterTypes;
458 }
459 }
460 }
461 }
462
463 if (method != null) {
464 methodAndParameterTypes = new Object[] {
465 method, methodParameterTypes
466 };
467
468 _methodCache.put(key, methodAndParameterTypes);
469
470 return methodAndParameterTypes;
471 }
472 else {
473 _log.error(
474 "No method found for class " + clazz + ", method " +
475 methodName + ", and parameters " + parameterNames);
476
477 return null;
478 }
479 }
480
481 @Override
482 protected String getReroutePath() {
483 return _REROUTE_PATH;
484 }
485
486 protected String getReturnValue(Object returnObj) throws Exception {
487 if (returnObj instanceof JSONSerializable) {
488 JSONSerializable jsonSerializable = (JSONSerializable)returnObj;
489
490 return jsonSerializable.toJSONString();
491 }
492
493 JSONSerializer jsonSerializer = JSONFactoryUtil.createJSONSerializer();
494
495 jsonSerializer.exclude("*.class");
496
497 return jsonSerializer.serialize(returnObj);
498 }
499
500 protected String[] getStringArrayFromJSON(
501 HttpServletRequest request, String param)
502 throws JSONException {
503
504 String json = ParamUtil.getString(request, param, "[]");
505
506 JSONArray jsonArray = JSONFactoryUtil.createJSONArray(json);
507
508 return ArrayUtil.toStringArray(jsonArray);
509 }
510
511 protected String getTypeNameOrClassDescriptor(Type type) {
512 String typeName = type.toString();
513
514 if (typeName.contains("class ")) {
515 return typeName.substring(6);
516 }
517
518 Matcher matcher = _fieldDescriptorPattern.matcher(typeName);
519
520 while (matcher.find()) {
521 String dimensions = matcher.group(2);
522 String fieldDescriptor = matcher.group(1);
523
524 if (Validator.isNull(dimensions)) {
525 return fieldDescriptor;
526 }
527
528 dimensions = dimensions.replace(
529 StringPool.CLOSE_BRACKET, StringPool.BLANK);
530
531 if (fieldDescriptor.equals("boolean")) {
532 fieldDescriptor = "Z";
533 }
534 else if (fieldDescriptor.equals("byte")) {
535 fieldDescriptor = "B";
536 }
537 else if (fieldDescriptor.equals("char")) {
538 fieldDescriptor = "C";
539 }
540 else if (fieldDescriptor.equals("double")) {
541 fieldDescriptor = "D";
542 }
543 else if (fieldDescriptor.equals("float")) {
544 fieldDescriptor = "F";
545 }
546 else if (fieldDescriptor.equals("int")) {
547 fieldDescriptor = "I";
548 }
549 else if (fieldDescriptor.equals("long")) {
550 fieldDescriptor = "J";
551 }
552 else if (fieldDescriptor.equals("short")) {
553 fieldDescriptor = "S";
554 }
555 else {
556 fieldDescriptor = "L".concat(fieldDescriptor).concat(
557 StringPool.SEMICOLON);
558 }
559
560 return dimensions.concat(fieldDescriptor);
561 }
562
563 throw new IllegalArgumentException(type.toString() + " is invalid");
564 }
565
566 protected boolean isValidRequest(HttpServletRequest request) {
567 String className = ParamUtil.getString(request, "serviceClassName");
568
569 if (className.contains(".service.") &&
570 className.endsWith("ServiceUtil") &&
571 !className.endsWith("LocalServiceUtil") &&
572 !_invalidClassNames.contains(className)) {
573
574 return true;
575 }
576 else {
577 return false;
578 }
579 }
580
581 private static final String _REROUTE_PATH = "/json";
582
583 private static Log _log = LogFactoryUtil.getLog(JSONServiceAction.class);
584
585 private static Pattern _fieldDescriptorPattern = Pattern.compile(
586 "^(.*?)((\\[\\])*)$", Pattern.DOTALL);
587
588 private Set<String> _invalidClassNames;
589 private Map<String, Object[]> _methodCache =
590 new HashMap<String, Object[]>();
591
592 }