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