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