001    /**
002     * Copyright (c) 2000-present 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.jsonwebservice;
016    
017    import com.liferay.portal.kernel.json.JSONFactoryUtil;
018    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
019    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
020    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceNaming;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.util.CamelCaseUtil;
024    import com.liferay.portal.kernel.util.GetterUtil;
025    import com.liferay.portal.kernel.util.LocaleUtil;
026    import com.liferay.portal.kernel.util.MethodParameter;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.service.ServiceContext;
029    
030    import java.lang.reflect.Array;
031    import java.lang.reflect.Method;
032    
033    import java.util.ArrayList;
034    import java.util.Calendar;
035    import java.util.Collection;
036    import java.util.HashMap;
037    import java.util.List;
038    import java.util.Locale;
039    import java.util.Map;
040    
041    import jodd.bean.BeanCopy;
042    import jodd.bean.BeanUtil;
043    
044    import jodd.typeconverter.TypeConversionException;
045    import jodd.typeconverter.TypeConverterManager;
046    
047    import jodd.util.NameValue;
048    import jodd.util.ReflectUtil;
049    
050    /**
051     * @author Igor Spasic
052     */
053    public class JSONWebServiceActionImpl implements JSONWebServiceAction {
054    
055            public JSONWebServiceActionImpl(
056                    JSONWebServiceActionConfig jsonWebServiceActionConfig,
057                    JSONWebServiceActionParameters jsonWebServiceActionParameters,
058                    JSONWebServiceNaming jsonWebServiceNaming) {
059    
060                    _jsonWebServiceActionConfig = jsonWebServiceActionConfig;
061                    _jsonWebServiceActionParameters = jsonWebServiceActionParameters;
062                    _jsonWebServiceNaming = jsonWebServiceNaming;
063            }
064    
065            @Override
066            public JSONWebServiceActionMapping getJSONWebServiceActionMapping() {
067                    return _jsonWebServiceActionConfig;
068            }
069    
070            @Override
071            public Object invoke() throws Exception {
072                    JSONRPCRequest jsonRPCRequest =
073                            _jsonWebServiceActionParameters.getJSONRPCRequest();
074    
075                    if (jsonRPCRequest == null) {
076                            return _invokeActionMethod();
077                    }
078    
079                    Object result = null;
080                    Exception exception = null;
081    
082                    try {
083                            result = _invokeActionMethod();
084                    }
085                    catch (Exception e) {
086                            exception = e;
087    
088                            _log.error(e, e);
089                    }
090    
091                    return new JSONRPCResponse(jsonRPCRequest, result, exception);
092            }
093    
094            private Object _convertListToArray(List<?> list, Class<?> componentType) {
095                    Object array = Array.newInstance(componentType, list.size());
096    
097                    for (int i = 0; i < list.size(); i++) {
098                            Object entry = list.get(i);
099    
100                            if (entry != null) {
101                                    entry = _convertType(entry, componentType);
102                            }
103    
104                            Array.set(array, i, entry);
105                    }
106    
107                    return array;
108            }
109    
110            private Object _convertType(Object inputObject, Class<?> targetType) {
111                    if (targetType == null) {
112                            return inputObject;
113                    }
114    
115                    Object outputObject = null;
116    
117                    try {
118                            outputObject = TypeConverterManager.convertType(
119                                    inputObject, targetType);
120                    }
121                    catch (TypeConversionException tce) {
122                            if (inputObject instanceof Map) {
123                                    try {
124                                            if (targetType.isInterface()) {
125                                                    Class<?> clazz = getClass();
126    
127                                                    ClassLoader classLoader = clazz.getClassLoader();
128    
129                                                    String modelClassName =
130                                                            _jsonWebServiceNaming.
131                                                                    convertModelClassToImplClassName(targetType);
132    
133                                                    try {
134                                                            targetType = classLoader.loadClass(modelClassName);
135                                                    }
136                                                    catch (ClassNotFoundException cnfe) {
137                                                    }
138                                            }
139    
140                                            outputObject = targetType.newInstance();
141    
142                                            BeanCopy beanCopy = BeanCopy.beans(
143                                                    inputObject, outputObject);
144    
145                                            beanCopy.copy();
146                                    }
147                                    catch (Exception e) {
148                                            throw new TypeConversionException(e);
149                                    }
150                            }
151    
152                            throw tce;
153                    }
154    
155                    return outputObject;
156            }
157    
158            private Object _convertValueToParameterValue(
159                    Object value, Class<?> parameterType,
160                    Class<?>[] genericParameterTypes) {
161    
162                    if (parameterType.isArray()) {
163                            List<?> list = null;
164    
165                            if (value instanceof List) {
166                                    list = (List<?>)value;
167                            }
168                            else {
169                                    String valueString = value.toString();
170    
171                                    valueString = valueString.trim();
172    
173                                    if (!valueString.startsWith(StringPool.OPEN_BRACKET)) {
174                                            valueString = StringPool.OPEN_BRACKET.concat(
175                                                    valueString).concat(StringPool.CLOSE_BRACKET);
176                                    }
177    
178                                    list = JSONFactoryUtil.looseDeserialize(
179                                            valueString, ArrayList.class);
180                            }
181    
182                            return _convertListToArray(list, parameterType.getComponentType());
183                    }
184                    else if (parameterType.equals(Calendar.class)) {
185                            Calendar calendar = Calendar.getInstance();
186    
187                            calendar.setLenient(false);
188    
189                            String valueString = value.toString();
190    
191                            valueString = valueString.trim();
192    
193                            long timeInMillis = GetterUtil.getLong(valueString);
194    
195                            calendar.setTimeInMillis(timeInMillis);
196    
197                            return calendar;
198                    }
199                    else if (Collection.class.isAssignableFrom(parameterType)) {
200                            List<?> list = null;
201    
202                            if (value instanceof List) {
203                                    list = (List<?>)value;
204                            }
205                            else {
206                                    String valueString = value.toString();
207    
208                                    valueString = valueString.trim();
209    
210                                    if (!valueString.startsWith(StringPool.OPEN_BRACKET)) {
211                                            valueString = StringPool.OPEN_BRACKET.concat(
212                                                    valueString).concat(StringPool.CLOSE_BRACKET);
213                                    }
214    
215                                    list = JSONFactoryUtil.looseDeserialize(
216                                            valueString, ArrayList.class);
217                            }
218    
219                            return _generifyList(list, genericParameterTypes);
220                    }
221                    else if (parameterType.equals(Locale.class)) {
222                            String valueString = value.toString();
223    
224                            valueString = valueString.trim();
225    
226                            return LocaleUtil.fromLanguageId(valueString);
227                    }
228                    else if (parameterType.equals(Map.class)) {
229                            Map<?, ?> map = null;
230    
231                            if (value instanceof Map) {
232                                    map = (Map<Object, Object>)value;
233                            }
234                            else {
235                                    String valueString = value.toString();
236    
237                                    valueString = valueString.trim();
238    
239                                    map = JSONFactoryUtil.looseDeserialize(
240                                            valueString, HashMap.class);
241                            }
242    
243                            return _generifyMap(map, genericParameterTypes);
244                    }
245                    else {
246                            Object parameterValue = null;
247    
248                            try {
249                                    parameterValue = _convertType(value, parameterType);
250                            }
251                            catch (Exception e1) {
252                                    if (value instanceof Map) {
253                                            try {
254                                                    parameterValue = _createDefaultParameterValue(
255                                                            null, parameterType);
256                                            }
257                                            catch (Exception e2) {
258                                                    throw new ClassCastException(e1.getMessage());
259                                            }
260    
261                                            BeanCopy beanCopy = BeanCopy.beans(value, parameterValue);
262    
263                                            beanCopy.copy();
264                                    }
265                                    else {
266                                            String valueString = value.toString();
267    
268                                            valueString = valueString.trim();
269    
270                                            if (!valueString.startsWith(StringPool.OPEN_CURLY_BRACE)) {
271                                                    throw new ClassCastException(e1.getMessage());
272                                            }
273    
274                                            parameterValue = JSONFactoryUtil.looseDeserialize(
275                                                    valueString, parameterType);
276                                    }
277                            }
278    
279                            return parameterValue;
280                    }
281            }
282    
283            private Object _createDefaultParameterValue(
284                            String parameterName, Class<?> parameterType)
285                    throws Exception {
286    
287                    if ((parameterName != null) && parameterName.equals("serviceContext") &&
288                            parameterType.equals(ServiceContext.class)) {
289    
290                            ServiceContext serviceContext =
291                                    _jsonWebServiceActionParameters.getServiceContext();
292    
293                            if (serviceContext == null) {
294                                    serviceContext = new ServiceContext();
295                            }
296    
297                            return serviceContext;
298                    }
299    
300                    String className = parameterType.getName();
301    
302                    if (className.contains("com.liferay") && className.contains("Util")) {
303                            throw new IllegalArgumentException(
304                                    "Not instantiating " + className);
305                    }
306    
307                    return parameterType.newInstance();
308            }
309    
310            private List<?> _generifyList(List<?> list, Class<?>[] types) {
311                    if (types == null) {
312                            return list;
313                    }
314    
315                    if (types.length != 1) {
316                            return list;
317                    }
318    
319                    List<Object> newList = new ArrayList<>(list.size());
320    
321                    for (Object entry : list) {
322                            if (entry != null) {
323                                    entry = _convertType(entry, types[0]);
324                            }
325    
326                            newList.add(entry);
327                    }
328    
329                    return newList;
330            }
331    
332            private Map<?, ?> _generifyMap(Map<?, ?> map, Class<?>[] types) {
333                    if (types == null) {
334                            return map;
335                    }
336    
337                    if (types.length != 2) {
338                            return map;
339                    }
340    
341                    Map<Object, Object> newMap = new HashMap<>(map.size());
342    
343                    for (Map.Entry<?, ?> entry : map.entrySet()) {
344                            Object key = _convertType(entry.getKey(), types[0]);
345    
346                            Object value = entry.getValue();
347    
348                            if (value != null) {
349                                    value = _convertType(value, types[1]);
350                            }
351    
352                            newMap.put(key, value);
353                    }
354    
355                    return newMap;
356            }
357    
358            private void _injectInnerParametersIntoValue(
359                    String parameterName, Object parameterValue) {
360    
361                    if (parameterValue == null) {
362                            return;
363                    }
364    
365                    List<NameValue<String, Object>> innerParameters =
366                            _jsonWebServiceActionParameters.getInnerParameters(parameterName);
367    
368                    if (innerParameters == null) {
369                            return;
370                    }
371    
372                    for (NameValue<String, Object> innerParameter : innerParameters) {
373                            try {
374                                    BeanUtil.setProperty(
375                                            parameterValue, innerParameter.getName(),
376                                            innerParameter.getValue());
377                            }
378                            catch (Exception e) {
379                                    if (_log.isDebugEnabled()) {
380                                            _log.debug(
381                                                    "Unable to set inner parameter " + parameterName + "." +
382                                                            innerParameter.getName(),
383                                                    e);
384                                    }
385                            }
386                    }
387            }
388    
389            private Object _invokeActionMethod() throws Exception {
390                    Object actionObject = _jsonWebServiceActionConfig.getActionObject();
391    
392                    Method actionMethod = _jsonWebServiceActionConfig.getActionMethod();
393    
394                    Class<?> actionClass = _jsonWebServiceActionConfig.getActionClass();
395    
396                    Object[] parameters = _prepareParameters(actionClass);
397    
398                    if (_jsonWebServiceActionConfig.isDeprecated() &&
399                            _log.isWarnEnabled()) {
400    
401                            _log.warn("Invoking deprecated method " + actionMethod.getName());
402                    }
403    
404                    return actionMethod.invoke(actionObject, parameters);
405            }
406    
407            private Object[] _prepareParameters(Class<?> actionClass) throws Exception {
408                    MethodParameter[] methodParameters =
409                            _jsonWebServiceActionConfig.getMethodParameters();
410    
411                    Object[] parameters = new Object[methodParameters.length];
412    
413                    for (int i = 0; i < methodParameters.length; i++) {
414                            String parameterName = methodParameters[i].getName();
415    
416                            parameterName = CamelCaseUtil.normalizeCamelCase(parameterName);
417    
418                            Object value = _jsonWebServiceActionParameters.getParameter(
419                                    parameterName);
420    
421                            Object parameterValue = null;
422    
423                            if (value != null) {
424                                    Class<?> parameterType = methodParameters[i].getType();
425    
426                                    String parameterTypeName =
427                                            _jsonWebServiceActionParameters.getParameterTypeName(
428                                                    parameterName);
429    
430                                    if (parameterTypeName != null) {
431                                            ClassLoader classLoader = actionClass.getClassLoader();
432    
433                                            parameterType = classLoader.loadClass(parameterTypeName);
434    
435                                            if (!ReflectUtil.isTypeOf(
436                                                            parameterType, methodParameters[i].getType())) {
437    
438                                                    throw new IllegalArgumentException(
439                                                            "Unmatched argument type " +
440                                                                    parameterType.getName() +
441                                                                            " for method argument " + i);
442                                            }
443                                    }
444    
445                                    if (value.equals(Void.TYPE)) {
446                                            parameterValue = _createDefaultParameterValue(
447                                                    parameterName, parameterType);
448                                    }
449                                    else {
450                                            parameterValue = _convertValueToParameterValue(
451                                                    value, parameterType,
452                                                    methodParameters[i].getGenericTypes());
453    
454                                            ServiceContext serviceContext =
455                                                    _jsonWebServiceActionParameters.getServiceContext();
456    
457                                            if ((serviceContext != null) &&
458                                                    parameterName.equals("serviceContext")) {
459    
460                                                    if ((parameterValue != null) &&
461                                                            (parameterValue instanceof ServiceContext)) {
462    
463                                                            serviceContext.merge(
464                                                                    (ServiceContext)parameterValue);
465                                                    }
466    
467                                                    parameterValue = serviceContext;
468                                            }
469                                    }
470                            }
471    
472                            _injectInnerParametersIntoValue(parameterName, parameterValue);
473    
474                            parameters[i] = parameterValue;
475                    }
476    
477                    return parameters;
478            }
479    
480            private static final Log _log = LogFactoryUtil.getLog(
481                    JSONWebServiceActionImpl.class);
482    
483            private final JSONWebServiceActionConfig _jsonWebServiceActionConfig;
484            private final JSONWebServiceActionParameters
485                    _jsonWebServiceActionParameters;
486            private final JSONWebServiceNaming _jsonWebServiceNaming;
487    
488    }