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.StringUtil;
027    import com.liferay.portal.service.ServiceContext;
028    
029    import java.lang.reflect.Method;
030    
031    import java.util.ArrayList;
032    import java.util.Calendar;
033    import java.util.HashMap;
034    import java.util.List;
035    import java.util.Locale;
036    import java.util.Map;
037    
038    import jodd.bean.BeanUtil;
039    
040    import jodd.typeconverter.TypeConverterManager;
041    
042    import jodd.util.KeyValue;
043    import jodd.util.ReflectUtil;
044    
045    /**
046     * @author Igor Spasic
047     */
048    public class JSONWebServiceActionImpl implements JSONWebServiceAction {
049    
050            public JSONWebServiceActionImpl(
051                    JSONWebServiceActionConfig jsonWebServiceActionConfig,
052                    JSONWebServiceActionParameters jsonWebServiceActionParameters) {
053    
054                    _jsonWebServiceActionConfig = jsonWebServiceActionConfig;
055                    _jsonWebServiceActionParameters = jsonWebServiceActionParameters;
056            }
057    
058            @Override
059            public JSONWebServiceActionMapping getJSONWebServiceActionMapping() {
060                    return _jsonWebServiceActionConfig;
061            }
062    
063            @Override
064            public Object invoke() throws Exception {
065                    JSONRPCRequest jsonRPCRequest =
066                            _jsonWebServiceActionParameters.getJSONRPCRequest();
067    
068                    if (jsonRPCRequest == null) {
069                            return _invokeActionMethod();
070                    }
071    
072                    Object result = null;
073                    Exception exception = null;
074    
075                    try {
076                            result = _invokeActionMethod();
077                    }
078                    catch (Exception e) {
079                            exception = e;
080    
081                            _log.error(e, e);
082                    }
083    
084                    return new JSONRPCResponse(jsonRPCRequest, result, exception);
085            }
086    
087            private Object _createDefaultParameterValue(
088                            String parameterName, Class<?> parameterType)
089                    throws Exception {
090    
091                    if (parameterName.equals("serviceContext") &&
092                            parameterType.equals(ServiceContext.class)) {
093    
094                            return new ServiceContext();
095                    }
096    
097                    String className = parameterType.getName();
098    
099                    if (className.contains("com.liferay") && className.contains("Util")) {
100                            throw new IllegalArgumentException(
101                                    "Not instantiating " + className);
102                    }
103    
104                    return parameterType.newInstance();
105            }
106    
107            private List<?> _generifyList(List<?> list, Class<?>[] types) {
108                    if (types == null) {
109                            return list;
110                    }
111    
112                    if (types.length != 1) {
113                            return list;
114                    }
115    
116                    List<Object> newList = new ArrayList<Object>(list.size());
117    
118                    for (Object entry : list) {
119                            if (entry != null) {
120                                    entry = TypeConverterManager.convertType(entry, types[0]);
121                            }
122    
123                            newList.add(entry);
124                    }
125    
126                    return newList;
127            }
128    
129            private Map<?, ?> _generifyMap(Map<?, ?> map, Class<?>[] types) {
130                    if (types == null) {
131                            return map;
132                    }
133    
134                    if (types.length != 2) {
135                            return map;
136                    }
137    
138                    Map<Object, Object> newMap = new HashMap<Object, Object>(map.size());
139    
140                    for (Map.Entry<?, ?> entry : map.entrySet()) {
141                            Object key = TypeConverterManager.convertType(
142                                    entry.getKey(), types[0]);
143    
144                            Object value = entry.getValue();
145    
146                            if (value != null) {
147                                    value = TypeConverterManager.convertType(value, types[1]);
148                            }
149    
150                            newMap.put(key, value);
151                    }
152    
153                    return newMap;
154            }
155    
156            private void _injectInnerParametersIntoValue(
157                    String parameterName, Object parameterValue) {
158    
159                    if (parameterValue == null) {
160                            return;
161                    }
162    
163                    List<KeyValue<String, Object>> innerParameters =
164                            _jsonWebServiceActionParameters.getInnerParameters(parameterName);
165    
166                    if (innerParameters == null) {
167                            return;
168                    }
169    
170                    for (KeyValue<String, Object> innerParameter : innerParameters) {
171                            try {
172                                    BeanUtil.setProperty(
173                                            parameterValue, innerParameter.getKey(),
174                                            innerParameter.getValue());
175                            }
176                            catch (Exception e) {
177                                    if (_log.isDebugEnabled()) {
178                                            _log.debug(
179                                                    "Unable to set inner parameter " + parameterName + "." +
180                                                            innerParameter.getKey(),
181                                                    e);
182                                    }
183                            }
184                    }
185            }
186    
187            private Object _invokeActionMethod() throws Exception {
188                    Method actionMethod = _jsonWebServiceActionConfig.getActionMethod();
189    
190                    Class<?> actionClass = _jsonWebServiceActionConfig.getActionClass();
191    
192                    Object[] parameters = _prepareParameters(actionClass);
193    
194                    return actionMethod.invoke(actionClass, parameters);
195            }
196    
197            private Object[] _prepareParameters(Class<?> actionClass) throws Exception {
198                    MethodParameter[] methodParameters =
199                            _jsonWebServiceActionConfig.getMethodParameters();
200    
201                    Object[] parameters = new Object[methodParameters.length];
202    
203                    for (int i = 0; i < methodParameters.length; i++) {
204                            String parameterName = methodParameters[i].getName();
205    
206                            parameterName = CamelCaseUtil.normalizeCamelCase(parameterName);
207    
208                            Object value = _jsonWebServiceActionParameters.getParameter(
209                                    parameterName);
210    
211                            Object parameterValue = null;
212    
213                            if (value != null) {
214                                    Class<?> parameterType = methodParameters[i].getType();
215    
216                                    if (value.equals(Void.TYPE)) {
217                                            String parameterTypeName =
218                                                    _jsonWebServiceActionParameters.getParameterTypeName(
219                                                            parameterName);
220    
221                                            if (parameterTypeName != null) {
222                                                    ClassLoader classLoader = actionClass.getClassLoader();
223    
224                                                    parameterType = classLoader.loadClass(
225                                                            parameterTypeName);
226                                            }
227    
228                                            if (!ReflectUtil.isSubclass(
229                                                            parameterType, methodParameters[i].getType())) {
230    
231                                                    throw new IllegalArgumentException(
232                                                            "Unmatched argument type " +
233                                                                    parameterType.getName() +
234                                                                            " for method argument " + i);
235                                            }
236    
237                                            parameterValue = _createDefaultParameterValue(
238                                                    parameterName, parameterType);
239                                    }
240                                    else if (parameterType.equals(Calendar.class)) {
241                                            Calendar calendar = Calendar.getInstance();
242    
243                                            calendar.setLenient(false);
244                                            calendar.setTimeInMillis(
245                                                    GetterUtil.getLong(_valueToString(value)));
246    
247                                            parameterValue = calendar;
248                                    }
249                                    else if (parameterType.equals(List.class)) {
250                                            List<?> list = JSONFactoryUtil.looseDeserializeSafe(
251                                                    _valueToString(value), ArrayList.class);
252    
253                                            list = _generifyList(
254                                                    list, methodParameters[i].getGenericTypes());
255    
256                                            parameterValue = list;
257                                    }
258                                    else if (parameterType.equals(Locale.class)) {
259                                            parameterValue = LocaleUtil.fromLanguageId(
260                                                    _valueToString(value));
261                                    }
262                                    else if (parameterType.equals(Map.class)) {
263                                            Map<?, ?> map = JSONFactoryUtil.looseDeserializeSafe(
264                                                    _valueToString(value), HashMap.class);
265    
266                                            map = _generifyMap(
267                                                    map, methodParameters[i].getGenericTypes());
268    
269                                            parameterValue = map;
270                                    }
271                                    else {
272                                            parameterValue = TypeConverterManager.convertType(
273                                                    value, parameterType);
274                                    }
275                            }
276    
277                            _injectInnerParametersIntoValue(parameterName, parameterValue);
278    
279                            parameters[i] = parameterValue;
280                    }
281    
282                    return parameters;
283            }
284    
285            private String _valueToString(Object value) {
286                    Class<?> valueType = value.getClass();
287    
288                    if (valueType.isArray()) {
289                            return StringUtil.merge((Object[])value);
290                    }
291    
292                    return value.toString();
293            }
294    
295            private static Log _log = LogFactoryUtil.getLog(
296                    JSONWebServiceActionImpl.class);
297    
298            private JSONWebServiceActionConfig _jsonWebServiceActionConfig;
299            private JSONWebServiceActionParameters _jsonWebServiceActionParameters;
300    
301    }