1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   * 
13   */
14  
15  package com.liferay.portal.action;
16  
17  import com.liferay.counter.service.CounterServiceUtil;
18  import com.liferay.documentlibrary.service.DLLocalServiceUtil;
19  import com.liferay.documentlibrary.service.DLServiceUtil;
20  import com.liferay.mail.service.MailServiceUtil;
21  import com.liferay.portal.kernel.json.JSONArray;
22  import com.liferay.portal.kernel.json.JSONException;
23  import com.liferay.portal.kernel.json.JSONFactoryUtil;
24  import com.liferay.portal.kernel.json.JSONObject;
25  import com.liferay.portal.kernel.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  import com.liferay.portal.kernel.util.ArrayUtil;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.MethodInvoker;
30  import com.liferay.portal.kernel.util.MethodWrapper;
31  import com.liferay.portal.kernel.util.ParamUtil;
32  import com.liferay.portal.kernel.util.StringUtil;
33  import com.liferay.portal.kernel.util.Validator;
34  import com.liferay.portal.model.BaseModel;
35  import com.liferay.portal.service.ServiceContext;
36  import com.liferay.portal.service.ServiceContextUtil;
37  import com.liferay.portal.struts.JSONAction;
38  import com.liferay.portlet.tags.model.TagsAssetDisplay;
39  import com.liferay.portlet.tags.model.TagsAssetType;
40  
41  import java.lang.reflect.InvocationTargetException;
42  import java.lang.reflect.Method;
43  
44  import java.util.Arrays;
45  import java.util.Date;
46  import java.util.HashMap;
47  import java.util.HashSet;
48  import java.util.List;
49  import java.util.Map;
50  import java.util.Set;
51  
52  import javax.servlet.http.HttpServletRequest;
53  import javax.servlet.http.HttpServletResponse;
54  
55  import org.apache.struts.action.ActionForm;
56  import org.apache.struts.action.ActionMapping;
57  
58  /**
59   * <a href="JSONServiceAction.java.html"><b><i>View Source</i></b></a>
60   *
61   * @author Brian Wing Shun Chan
62   * @author Karthik Sudarshan
63   */
64  public class JSONServiceAction extends JSONAction {
65  
66      public static JSONObject toJSONObject(TagsAssetDisplay assetDisplay) {
67          JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
68  
69          jsonObj.put("assetId", assetDisplay.getAssetId());
70          jsonObj.put("companyId", assetDisplay.getCompanyId());
71          jsonObj.put("userId", assetDisplay.getUserId());
72          jsonObj.put("userName", assetDisplay.getUserName());
73          jsonObj.put("createDate", assetDisplay.getCreateDate());
74          jsonObj.put("modifiedDate", assetDisplay.getModifiedDate());
75          jsonObj.put("classNameId", assetDisplay.getClassNameId());
76          jsonObj.put("className", assetDisplay.getClassName());
77          jsonObj.put("classPK", assetDisplay.getClassPK());
78          jsonObj.put("portletId", assetDisplay.getPortletId());
79          jsonObj.put("portletTitle", assetDisplay.getPortletTitle());
80          jsonObj.put("startDate", assetDisplay.getStartDate());
81          jsonObj.put("endDate", assetDisplay.getEndDate());
82          jsonObj.put("publishDate", assetDisplay.getPublishDate());
83          jsonObj.put("expirationDate", assetDisplay.getExpirationDate());
84          jsonObj.put("mimeType", assetDisplay.getMimeType());
85          jsonObj.put("title", assetDisplay.getTitle());
86          jsonObj.put("description", assetDisplay.getDescription());
87          jsonObj.put("summary", assetDisplay.getSummary());
88          jsonObj.put("url", assetDisplay.getUrl());
89          jsonObj.put("height", assetDisplay.getHeight());
90          jsonObj.put("width", assetDisplay.getWidth());
91          jsonObj.put("priority", assetDisplay.getPriority());
92          jsonObj.put("viewCount", assetDisplay.getViewCount());
93          jsonObj.put("tagsEntries", assetDisplay.getTagsEntries());
94  
95          return jsonObj;
96      }
97  
98      public static JSONObject toJSONObject(TagsAssetType assetType) {
99          JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
100 
101         jsonObj.put("classNameId", assetType.getClassNameId());
102         jsonObj.put("className", assetType.getClassName());
103         jsonObj.put("portletId", assetType.getPortletId());
104         jsonObj.put("portletTitle", assetType.getPortletTitle());
105 
106         return jsonObj;
107     }
108 
109     public JSONServiceAction() {
110         _invalidClassNames.add(CounterServiceUtil.class.getName());
111         _invalidClassNames.add(DLLocalServiceUtil.class.getName());
112         _invalidClassNames.add(DLServiceUtil.class.getName());
113         _invalidClassNames.add(MailServiceUtil.class.getName());
114     }
115 
116     public String getJSON(
117             ActionMapping mapping, ActionForm form, HttpServletRequest request,
118             HttpServletResponse response)
119         throws Exception {
120 
121         String className = ParamUtil.getString(request, "serviceClassName");
122         String methodName = ParamUtil.getString(request, "serviceMethodName");
123         String[] serviceParameters = getStringArrayFromJSON(
124             request, "serviceParameters");
125         String[] serviceParameterTypes = getStringArrayFromJSON(
126             request, "serviceParameterTypes");
127 
128         if (!isValidRequest(request)) {
129             return null;
130         }
131 
132         Class<?> classObj = Class.forName(className);
133 
134         Object[] methodAndParameterTypes = getMethodAndParameterTypes(
135             classObj, methodName, serviceParameters, serviceParameterTypes);
136 
137         if (methodAndParameterTypes != null) {
138             Method method = (Method)methodAndParameterTypes[0];
139             Class<?>[] parameterTypes = (Class[])methodAndParameterTypes[1];
140             Object[] args = new Object[serviceParameters.length];
141 
142             for (int i = 0; i < serviceParameters.length; i++) {
143                 args[i] = getArgValue(
144                     request, classObj, methodName, serviceParameters[i],
145                     parameterTypes[i]);
146             }
147 
148             try {
149                 if (_log.isDebugEnabled()) {
150                     _log.debug(
151                         "Invoking class " + classObj + " on method " +
152                             method.getName() + " with args " +
153                                 Arrays.toString(args));
154                 }
155 
156                 Object returnObj = method.invoke(classObj, args);
157 
158                 if (returnObj != null) {
159                     return getReturnValue(returnObj);
160                 }
161                 else {
162                     JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
163 
164                     return jsonObj.toString();
165                 }
166             }
167             catch (Exception e) {
168                 if (_log.isDebugEnabled()) {
169                     _log.debug(
170                         "Invoked class " + classObj + " on method " +
171                             method.getName() + " with args " +
172                                 Arrays.toString(args),
173                         e);
174                 }
175 
176                 JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
177 
178                 if (e instanceof InvocationTargetException) {
179                     jsonObj.put("exception", e.getCause().toString());
180                 }
181                 else {
182                     jsonObj.put("exception", e.getMessage());
183                 }
184 
185                 return jsonObj.toString();
186             }
187         }
188 
189         return null;
190     }
191 
192     protected Object getArgValue(
193             HttpServletRequest request, Class<?> classObj, String methodName,
194             String parameter, Class<?> parameterType)
195         throws Exception {
196 
197         String parameterTypeName = parameterType.getName();
198 
199         String value = ParamUtil.getString(request, parameter);
200 
201         if (Validator.isNull(value) &&
202             !parameterTypeName.equals("[Ljava.lang.String;")) {
203 
204             return null;
205         }
206         else if (parameterTypeName.equals("boolean") ||
207                  parameterTypeName.equals(Boolean.class.getName())) {
208 
209             return Boolean.valueOf(ParamUtil.getBoolean(request, parameter));
210         }
211         else if (parameterTypeName.equals("double") ||
212                  parameterTypeName.equals(Double.class.getName())) {
213 
214             return new Double(ParamUtil.getDouble(request, parameter));
215         }
216         else if (parameterTypeName.equals("int") ||
217                  parameterTypeName.equals(Integer.class.getName())) {
218 
219             return new Integer(ParamUtil.getInteger(request, parameter));
220         }
221         else if (parameterTypeName.equals("long") ||
222                  parameterTypeName.equals(Long.class.getName())) {
223 
224             return new Long(ParamUtil.getLong(request, parameter));
225         }
226         else if (parameterTypeName.equals("short") ||
227                  parameterTypeName.equals(Short.class.getName())) {
228 
229             return new Short(ParamUtil.getShort(request, parameter));
230         }
231         else if (parameterTypeName.equals(Date.class.getName())) {
232             return new Date(ParamUtil.getLong(request, parameter));
233         }
234         else if (parameterTypeName.equals(ServiceContext.class.getName())) {
235             JSONObject jsonObject = JSONFactoryUtil.createJSONObject(value);
236 
237             jsonObject.put("javaClass", ServiceContext.class.getName());
238 
239             return ServiceContextUtil.deserialize(jsonObject);
240         }
241         else if (parameterTypeName.equals(String.class.getName())) {
242             return value;
243         }
244         else if (parameterTypeName.equals("[Z")) {
245             return ParamUtil.getBooleanValues(request, parameter);
246         }
247         else if (parameterTypeName.equals("[D")) {
248             return ParamUtil.getDoubleValues(request, parameter);
249         }
250         else if (parameterTypeName.equals("[F")) {
251             return ParamUtil.getFloatValues(request, parameter);
252         }
253         else if (parameterTypeName.equals("[I")) {
254             return ParamUtil.getIntegerValues(request, parameter);
255         }
256         else if (parameterTypeName.equals("[J")) {
257             return ParamUtil.getLongValues(request, parameter);
258         }
259         else if (parameterTypeName.equals("[S")) {
260             return ParamUtil.getShortValues(request, parameter);
261         }
262         else if (parameterTypeName.equals("[Ljava.lang.String;")) {
263             return StringUtil.split(value);
264         }
265         else if (parameterTypeName.equals("[[Z")) {
266             String[] values = request.getParameterValues(parameter);
267 
268             if ((values != null) && (values.length > 0)) {
269                 String[] values0 = StringUtil.split(values[0]);
270 
271                 boolean[][] doubleArray =
272                     new boolean[values.length][values0.length];
273 
274                 for (int i = 0; i < values.length; i++) {
275                     String[] curValues = StringUtil.split(values[i]);
276 
277                     for (int j = 0; j < curValues.length; j++) {
278                         doubleArray[i][j] = GetterUtil.getBoolean(curValues[j]);
279                     }
280                 }
281 
282                 return doubleArray;
283             }
284             else {
285                 return new boolean[0][0];
286             }
287         }
288         else if (parameterTypeName.equals("[[D")) {
289             String[] values = request.getParameterValues(parameter);
290 
291             if ((values != null) && (values.length > 0)) {
292                 String[] values0 = StringUtil.split(values[0]);
293 
294                 double[][] doubleArray =
295                     new double[values.length][values0.length];
296 
297                 for (int i = 0; i < values.length; i++) {
298                     String[] curValues = StringUtil.split(values[i]);
299 
300                     for (int j = 0; j < curValues.length; j++) {
301                         doubleArray[i][j] = GetterUtil.getDouble(curValues[j]);
302                     }
303                 }
304 
305                 return doubleArray;
306             }
307             else {
308                 return new double[0][0];
309             }
310         }
311         else if (parameterTypeName.equals("[[F")) {
312             String[] values = request.getParameterValues(parameter);
313 
314             if ((values != null) && (values.length > 0)) {
315                 String[] values0 = StringUtil.split(values[0]);
316 
317                 float[][] doubleArray =
318                     new float[values.length][values0.length];
319 
320                 for (int i = 0; i < values.length; i++) {
321                     String[] curValues = StringUtil.split(values[i]);
322 
323                     for (int j = 0; j < curValues.length; j++) {
324                         doubleArray[i][j] = GetterUtil.getFloat(curValues[j]);
325                     }
326                 }
327 
328                 return doubleArray;
329             }
330             else {
331                 return new float[0][0];
332             }
333         }
334         else if (parameterTypeName.equals("[[I")) {
335             String[] values = request.getParameterValues(parameter);
336 
337             if ((values != null) && (values.length > 0)) {
338                 String[] values0 = StringUtil.split(values[0]);
339 
340                 int[][] doubleArray =
341                     new int[values.length][values0.length];
342 
343                 for (int i = 0; i < values.length; i++) {
344                     String[] curValues = StringUtil.split(values[i]);
345 
346                     for (int j = 0; j < curValues.length; j++) {
347                         doubleArray[i][j] = GetterUtil.getInteger(curValues[j]);
348                     }
349                 }
350 
351                 return doubleArray;
352             }
353             else {
354                 return new int[0][0];
355             }
356         }
357         else if (parameterTypeName.equals("[[J")) {
358             String[] values = request.getParameterValues(parameter);
359 
360             if ((values != null) && (values.length > 0)) {
361                 String[] values0 = StringUtil.split(values[0]);
362 
363                 long[][] doubleArray =
364                     new long[values.length][values0.length];
365 
366                 for (int i = 0; i < values.length; i++) {
367                     String[] curValues = StringUtil.split(values[i]);
368 
369                     for (int j = 0; j < curValues.length; j++) {
370                         doubleArray[i][j] = GetterUtil.getLong(curValues[j]);
371                     }
372                 }
373 
374                 return doubleArray;
375             }
376             else {
377                 return new long[0][0];
378             }
379         }
380         else if (parameterTypeName.equals("[[S")) {
381             String[] values = request.getParameterValues(parameter);
382 
383             if ((values != null) && (values.length > 0)) {
384                 String[] values0 = StringUtil.split(values[0]);
385 
386                 short[][] doubleArray =
387                     new short[values.length][values0.length];
388 
389                 for (int i = 0; i < values.length; i++) {
390                     String[] curValues = StringUtil.split(values[i]);
391 
392                     for (int j = 0; j < curValues.length; j++) {
393                         doubleArray[i][j] = GetterUtil.getShort(curValues[j]);
394                     }
395                 }
396 
397                 return doubleArray;
398             }
399             else {
400                 return new short[0][0];
401             }
402         }
403         else if (parameterTypeName.equals("[[Ljava.lang.String")) {
404             String[] values = request.getParameterValues(parameter);
405 
406             if ((values != null) && (values.length > 0)) {
407                 String[] values0 = StringUtil.split(values[0]);
408 
409                 String[][] doubleArray =
410                     new String[values.length][values0.length];
411 
412                 for (int i = 0; i < values.length; i++) {
413                     doubleArray[i] = StringUtil.split(values[i]);
414                 }
415 
416                 return doubleArray;
417             }
418             else {
419                 return new String[0][0];
420             }
421         }
422         else {
423             _log.error(
424                 "Unsupported parameter type for class " + classObj +
425                     ", method " + methodName + ", parameter " + parameter +
426                         ", and type " + parameterTypeName);
427 
428             return null;
429         }
430     }
431 
432     protected Object[] getMethodAndParameterTypes(
433             Class<?> classObj, String methodName, String[] parameters,
434             String[] parameterTypes)
435         throws Exception {
436 
437         String parameterNames = StringUtil.merge(parameters);
438 
439         String key =
440             classObj.getName() + "_METHOD_NAME_" + methodName +
441                 "_PARAMETERS_" + parameterNames;
442 
443         Object[] methodAndParameterTypes = _methodCache.get(key);
444 
445         if (methodAndParameterTypes != null) {
446             return methodAndParameterTypes;
447         }
448 
449         Method method = null;
450         Class<?>[] methodParameterTypes = null;
451 
452         Method[] methods = classObj.getMethods();
453 
454         for (int i = 0; i < methods.length; i++) {
455             Method curMethod = methods[i];
456 
457             if (curMethod.getName().equals(methodName)) {
458                 Class<?>[] curParameterTypes = curMethod.getParameterTypes();
459 
460                 if (curParameterTypes.length == parameters.length) {
461                     if ((parameterTypes.length > 0) &&
462                         (parameterTypes.length == curParameterTypes.length)) {
463 
464                         boolean match = true;
465 
466                         for (int j = 0; j < parameterTypes.length; j++) {
467                             String t1 = parameterTypes[j];
468                             String t2 = curParameterTypes[j].getName();
469 
470                             if (!t1.equals(t2)) {
471                                 match = false;
472                             }
473                         }
474 
475                         if (match) {
476                             method = curMethod;
477                             methodParameterTypes = curParameterTypes;
478 
479                             break;
480                         }
481                     }
482                     else if (method != null) {
483                         _log.error(
484                             "Obscure method name for class " + classObj +
485                                 ", method " + methodName + ", and parameters " +
486                                     parameterNames);
487 
488                         return null;
489                     }
490                     else {
491                         method = curMethod;
492                         methodParameterTypes = curParameterTypes;
493                     }
494                 }
495             }
496         }
497 
498         if (method != null) {
499             methodAndParameterTypes =
500                 new Object[] {method, methodParameterTypes};
501 
502             _methodCache.put(key, methodAndParameterTypes);
503 
504             return methodAndParameterTypes;
505         }
506         else {
507             _log.error(
508                 "No method found for class " + classObj + ", method " +
509                     methodName + ", and parameters " + parameterNames);
510 
511             return null;
512         }
513     }
514 
515     protected String getReturnValue(Object returnObj) throws Exception {
516         if ((returnObj instanceof Boolean) || (returnObj instanceof Double) ||
517             (returnObj instanceof Integer) || (returnObj instanceof Long) ||
518             (returnObj instanceof Short) || (returnObj instanceof String)) {
519 
520             JSONObject jsonObj = JSONFactoryUtil.createJSONObject();
521 
522             jsonObj.put("returnValue", returnObj.toString());
523 
524             return jsonObj.toString();
525         }
526         else if (returnObj instanceof BaseModel<?>) {
527             String serlializerClassName = getSerializerClassName(returnObj);
528 
529             MethodWrapper methodWrapper = new MethodWrapper(
530                 serlializerClassName, "toJSONObject", returnObj);
531 
532             JSONObject jsonObj = (JSONObject)MethodInvoker.invoke(
533                 methodWrapper, false);
534 
535             return jsonObj.toString();
536         }
537         else if (returnObj instanceof BaseModel<?>[]) {
538             JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
539 
540             BaseModel<?>[] returnArray = (BaseModel[])returnObj;
541 
542             if (returnArray.length > 0) {
543                 BaseModel<?> returnItem0 = returnArray[0];
544 
545                 String serializerClassName = getSerializerClassName(
546                     returnItem0);
547 
548                 MethodWrapper methodWrapper = new MethodWrapper(
549                     serializerClassName, "toJSONArray", returnObj);
550 
551                 jsonArray = (JSONArray)MethodInvoker.invoke(
552                     methodWrapper, false);
553             }
554 
555             return jsonArray.toString();
556         }
557         else if (returnObj instanceof BaseModel<?>[][]) {
558             JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
559 
560             BaseModel<?>[][] returnArray = (BaseModel<?>[][])returnObj;
561 
562             if ((returnArray.length > 0) &&
563                 (returnArray[0].length > 0)) {
564 
565                 BaseModel<?> returnItem0 = returnArray[0][0];
566 
567                 String serializerClassName = getSerializerClassName(
568                     returnItem0);
569 
570                 MethodWrapper methodWrapper = new MethodWrapper(
571                     serializerClassName, "toJSONArray", returnObj);
572 
573                 jsonArray = (JSONArray)MethodInvoker.invoke(
574                     methodWrapper, false);
575             }
576 
577             return jsonArray.toString();
578         }
579         else if (returnObj instanceof List<?>) {
580             JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
581 
582             List<Object> returnList = (List<Object>)returnObj;
583 
584             if (!returnList.isEmpty()) {
585                 Object returnItem0 = returnList.get(0);
586 
587                 String serlializerClassName = getSerializerClassName(
588                     returnItem0);
589 
590                 MethodWrapper methodWrapper = new MethodWrapper(
591                     serlializerClassName, "toJSONArray", returnObj);
592 
593                 jsonArray = (JSONArray)MethodInvoker.invoke(
594                     methodWrapper, false);
595             }
596 
597             return jsonArray.toString();
598         }
599         else if (returnObj instanceof JSONArray) {
600             JSONArray jsonArray = (JSONArray)returnObj;
601 
602             return jsonArray.toString();
603         }
604         else if (returnObj instanceof JSONObject) {
605             JSONObject jsonObj = (JSONObject)returnObj;
606 
607             return jsonObj.toString();
608         }
609         else if (returnObj instanceof TagsAssetDisplay) {
610             return getReturnValue((TagsAssetDisplay)returnObj);
611         }
612         else if (returnObj instanceof TagsAssetDisplay[]) {
613             return getReturnValue((TagsAssetDisplay[])returnObj);
614         }
615         else if (returnObj instanceof TagsAssetType) {
616             return getReturnValue((TagsAssetType)returnObj);
617         }
618         else if (returnObj instanceof TagsAssetType[]) {
619             return getReturnValue((TagsAssetType[])returnObj);
620         }
621         else {
622             return JSONFactoryUtil.serialize(returnObj);
623         }
624     }
625 
626     protected String getReturnValue(TagsAssetDisplay assetDisplay)
627         throws Exception {
628 
629         JSONObject jsonObj = toJSONObject(assetDisplay);
630 
631         return jsonObj.toString();
632     }
633 
634     protected String getReturnValue(TagsAssetDisplay[] assetDisplays)
635         throws Exception {
636 
637         JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
638 
639         for (int i = 0; i < assetDisplays.length; i++) {
640             TagsAssetDisplay assetDisplay = assetDisplays[i];
641 
642             jsonArray.put(toJSONObject(assetDisplay));
643         }
644 
645         return jsonArray.toString();
646     }
647 
648     protected String getReturnValue(TagsAssetType assetType)
649         throws Exception {
650 
651         JSONObject jsonObj = toJSONObject(assetType);
652 
653         return jsonObj.toString();
654     }
655 
656     protected String getReturnValue(TagsAssetType[] assetTypes)
657         throws Exception {
658 
659         JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
660 
661         for (int i = 0; i < assetTypes.length; i++) {
662             TagsAssetType assetType = assetTypes[i];
663 
664             jsonArray.put(toJSONObject(assetType));
665         }
666 
667         return jsonArray.toString();
668     }
669 
670     protected String getSerializerClassName(Object obj) {
671         String serlializerClassName = StringUtil.replace(
672             obj.getClass().getName(),
673             new String[] {".model.impl.", "Impl"},
674             new String[] {".service.http.", "JSONSerializer"});
675 
676         return serlializerClassName;
677     }
678 
679     protected String[] getStringArrayFromJSON(
680             HttpServletRequest request, String param)
681         throws JSONException {
682 
683         String json = ParamUtil.getString(request, param, "[]");
684 
685         JSONArray jsonArray = JSONFactoryUtil.createJSONArray(json);
686 
687         return ArrayUtil.toStringArray(jsonArray);
688     }
689 
690     protected boolean isValidRequest(HttpServletRequest request) {
691         String className = ParamUtil.getString(request, "serviceClassName");
692 
693         if (className.contains(".service.") &&
694             className.endsWith("ServiceUtil") &&
695             !className.endsWith("LocalServiceUtil") &&
696             !_invalidClassNames.contains(className)) {
697 
698             return true;
699         }
700         else {
701             return false;
702         }
703     }
704 
705     private static Log _log = LogFactoryUtil.getLog(JSONServiceAction.class);
706 
707     private Set<String> _invalidClassNames = new HashSet<String>();
708     private Map<String, Object[]> _methodCache =
709         new HashMap<String, Object[]>();
710 
711 }