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