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