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