/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.jsonwebservice; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction; import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping; import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceNaming; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.util.CamelCaseUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.MethodParameter; import com.liferay.portal.kernel.util.StringPool; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import jodd.bean.BeanCopy; import jodd.bean.BeanUtil; import jodd.typeconverter.TypeConversionException; import jodd.typeconverter.TypeConverterManager; import jodd.util.NameValue; import jodd.util.ReflectUtil; /** * @author Igor Spasic */ public class JSONWebServiceActionImpl implements JSONWebServiceAction { public JSONWebServiceActionImpl( JSONWebServiceActionConfig jsonWebServiceActionConfig, JSONWebServiceActionParameters jsonWebServiceActionParameters, JSONWebServiceNaming jsonWebServiceNaming) { _jsonWebServiceActionConfig = jsonWebServiceActionConfig; _jsonWebServiceActionParameters = jsonWebServiceActionParameters; _jsonWebServiceNaming = jsonWebServiceNaming; } @Override public JSONWebServiceActionMapping getJSONWebServiceActionMapping() { return _jsonWebServiceActionConfig; } @Override public Object invoke() throws Exception { JSONRPCRequest jsonRPCRequest = _jsonWebServiceActionParameters.getJSONRPCRequest(); if (jsonRPCRequest == null) { return _invokeActionMethod(); } Object result = null; Exception exception = null; try { result = _invokeActionMethod(); } catch (Exception e) { exception = e; _log.error(e, e); } return new JSONRPCResponse(jsonRPCRequest, result, exception); } private Object _convertListToArray(List<?> list, Class<?> componentType) { Object array = Array.newInstance(componentType, list.size()); for (int i = 0; i < list.size(); i++) { Object entry = list.get(i); if (entry != null) { entry = _convertType(entry, componentType); } Array.set(array, i, entry); } return array; } private Object _convertType(Object inputObject, Class<?> targetType) { if (targetType == null) { return inputObject; } Object outputObject = null; try { outputObject = TypeConverterManager.convertType( inputObject, targetType); } catch (TypeConversionException tce) { if (inputObject instanceof Map) { try { if (targetType.isInterface()) { Class<?> clazz = getClass(); ClassLoader classLoader = clazz.getClassLoader(); String modelClassName = _jsonWebServiceNaming. convertModelClassToImplClassName(targetType); try { targetType = classLoader.loadClass(modelClassName); } catch (ClassNotFoundException cnfe) { } } outputObject = targetType.newInstance(); BeanCopy beanCopy = BeanCopy.beans( inputObject, outputObject); beanCopy.copy(); return outputObject; } catch (Exception e) { throw new TypeConversionException(e); } } throw tce; } return outputObject; } private Object _convertValueToParameterValue( Object value, Class<?> parameterType, Class<?>[] genericParameterTypes) { if (parameterType.isArray()) { List<?> list = null; if (value instanceof List) { list = (List<?>)value; } else { String valueString = value.toString(); valueString = valueString.trim(); if (!valueString.startsWith(StringPool.OPEN_BRACKET)) { valueString = StringPool.OPEN_BRACKET.concat( valueString).concat(StringPool.CLOSE_BRACKET); } list = JSONFactoryUtil.looseDeserialize( valueString, ArrayList.class); } return _convertListToArray(list, parameterType.getComponentType()); } else if (parameterType.equals(Calendar.class)) { Calendar calendar = Calendar.getInstance(); calendar.setLenient(false); String valueString = value.toString(); valueString = valueString.trim(); long timeInMillis = GetterUtil.getLong(valueString); calendar.setTimeInMillis(timeInMillis); return calendar; } else if (Collection.class.isAssignableFrom(parameterType)) { List<?> list = null; if (value instanceof List) { list = (List<?>)value; } else { String valueString = value.toString(); valueString = valueString.trim(); if (!valueString.startsWith(StringPool.OPEN_BRACKET)) { valueString = StringPool.OPEN_BRACKET.concat( valueString).concat(StringPool.CLOSE_BRACKET); } list = JSONFactoryUtil.looseDeserialize( valueString, ArrayList.class); } return _generifyList(list, genericParameterTypes); } else if (parameterType.equals(Locale.class)) { String valueString = value.toString(); valueString = valueString.trim(); return LocaleUtil.fromLanguageId(valueString); } else if (parameterType.equals(Map.class)) { Map<?, ?> map = null; if (value instanceof Map) { map = (Map<Object, Object>)value; } else { String valueString = value.toString(); valueString = valueString.trim(); map = JSONFactoryUtil.looseDeserialize( valueString, HashMap.class); } return _generifyMap(map, genericParameterTypes); } else { Object parameterValue = null; try { parameterValue = _convertType(value, parameterType); } catch (Exception e1) { if (value instanceof Map) { try { parameterValue = _createDefaultParameterValue( null, parameterType); } catch (Exception e2) { throw new ClassCastException(e1.getMessage()); } BeanCopy beanCopy = BeanCopy.beans(value, parameterValue); beanCopy.copy(); } else { String valueString = value.toString(); valueString = valueString.trim(); if (!valueString.startsWith(StringPool.OPEN_CURLY_BRACE)) { throw new ClassCastException(e1.getMessage()); } parameterValue = JSONFactoryUtil.looseDeserialize( valueString, parameterType); } } return parameterValue; } } private Object _createDefaultParameterValue( String parameterName, Class<?> parameterType) throws Exception { if ((parameterName != null) && parameterName.equals("serviceContext") && parameterType.equals(ServiceContext.class)) { ServiceContext serviceContext = _jsonWebServiceActionParameters.getServiceContext(); if (serviceContext == null) { serviceContext = new ServiceContext(); } return serviceContext; } String className = parameterType.getName(); if (className.contains("com.liferay") && className.contains("Util")) { throw new IllegalArgumentException( "Not instantiating " + className); } return parameterType.newInstance(); } private List<?> _generifyList(List<?> list, Class<?>[] types) { if (types == null) { return list; } if (types.length != 1) { return list; } List<Object> newList = new ArrayList<>(list.size()); for (Object entry : list) { if (entry != null) { entry = _convertType(entry, types[0]); } newList.add(entry); } return newList; } private Map<?, ?> _generifyMap(Map<?, ?> map, Class<?>[] types) { if (types == null) { return map; } if (types.length != 2) { return map; } Map<Object, Object> newMap = new HashMap<>(map.size()); for (Map.Entry<?, ?> entry : map.entrySet()) { Object key = _convertType(entry.getKey(), types[0]); Object value = entry.getValue(); if (value != null) { value = _convertType(value, types[1]); } newMap.put(key, value); } return newMap; } private void _injectInnerParametersIntoValue( String parameterName, Object parameterValue) { if (parameterValue == null) { return; } List<NameValue<String, Object>> innerParameters = _jsonWebServiceActionParameters.getInnerParameters(parameterName); if (innerParameters == null) { return; } for (NameValue<String, Object> innerParameter : innerParameters) { try { BeanUtil.setProperty( parameterValue, innerParameter.getName(), innerParameter.getValue()); } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug( "Unable to set inner parameter " + parameterName + "." + innerParameter.getName(), e); } } } } private Object _invokeActionMethod() throws Exception { Object actionObject = _jsonWebServiceActionConfig.getActionObject(); Method actionMethod = _jsonWebServiceActionConfig.getActionMethod(); Class<?> actionClass = _jsonWebServiceActionConfig.getActionClass(); Object[] parameters = _prepareParameters(actionClass); if (_jsonWebServiceActionConfig.isDeprecated() && _log.isWarnEnabled()) { _log.warn("Invoking deprecated method " + actionMethod.getName()); } return actionMethod.invoke(actionObject, parameters); } private Object[] _prepareParameters(Class<?> actionClass) throws Exception { MethodParameter[] methodParameters = _jsonWebServiceActionConfig.getMethodParameters(); Object[] parameters = new Object[methodParameters.length]; for (int i = 0; i < methodParameters.length; i++) { String parameterName = methodParameters[i].getName(); parameterName = CamelCaseUtil.normalizeCamelCase(parameterName); Object value = _jsonWebServiceActionParameters.getParameter( parameterName); Object parameterValue = null; if (value != null) { Class<?> parameterType = methodParameters[i].getType(); String parameterTypeName = _jsonWebServiceActionParameters.getParameterTypeName( parameterName); if (parameterTypeName != null) { ClassLoader classLoader = actionClass.getClassLoader(); parameterType = classLoader.loadClass(parameterTypeName); if (!ReflectUtil.isTypeOf( parameterType, methodParameters[i].getType())) { throw new IllegalArgumentException( "Unmatched argument type " + parameterType.getName() + " for method argument " + i); } } if (value.equals(Void.TYPE)) { parameterValue = _createDefaultParameterValue( parameterName, parameterType); } else { parameterValue = _convertValueToParameterValue( value, parameterType, methodParameters[i].getGenericTypes()); ServiceContext serviceContext = _jsonWebServiceActionParameters.getServiceContext(); if ((serviceContext != null) && parameterName.equals("serviceContext")) { if ((parameterValue != null) && (parameterValue instanceof ServiceContext)) { serviceContext.merge( (ServiceContext)parameterValue); } parameterValue = serviceContext; } } } _injectInnerParametersIntoValue(parameterName, parameterValue); parameters[i] = parameterValue; } return parameters; } private static final Log _log = LogFactoryUtil.getLog( JSONWebServiceActionImpl.class); private final JSONWebServiceActionConfig _jsonWebServiceActionConfig; private final JSONWebServiceActionParameters _jsonWebServiceActionParameters; private final JSONWebServiceNaming _jsonWebServiceNaming; }