/**
* 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.bean.BeanLocator;
import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
import com.liferay.portal.kernel.bean.PortletBeanLocatorUtil;
import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManager;
import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceNaming;
import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceRegistrator;
import com.liferay.portal.kernel.jsonwebservice.NoSuchJSONWebServiceException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.pacl.DoPrivileged;
import com.liferay.portal.kernel.service.ServiceContextThreadLocal;
import com.liferay.portal.kernel.servlet.HttpMethods;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.MethodParameter;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.spring.context.PortalContextLoaderListener;
import com.liferay.portal.util.PropsValues;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/**
* @author Igor Spasic
* @author Raymond Augé
*/
@DoPrivileged
public class JSONWebServiceActionsManagerImpl
implements JSONWebServiceActionsManager {
@Override
public Set<String> getContextNames() {
return new TreeSet<>(
_contextNameIndexedJSONWebServiceActionConfigs.keySet());
}
@Override
public JSONWebServiceAction getJSONWebServiceAction(
HttpServletRequest request)
throws NoSuchJSONWebServiceException {
String path = GetterUtil.getString(request.getPathInfo());
String method = GetterUtil.getString(request.getMethod());
String parameterPath = null;
JSONRPCRequest jsonRPCRequest = null;
int parameterPathIndex = _getParameterPathIndex(path);
if (parameterPathIndex != -1) {
parameterPath = path.substring(parameterPathIndex);
path = path.substring(0, parameterPathIndex);
}
else {
if (method.equals(HttpMethods.POST) &&
!PortalUtil.isMultipartRequest(request)) {
jsonRPCRequest = JSONRPCRequest.detectJSONRPCRequest(request);
if (jsonRPCRequest != null) {
path += StringPool.SLASH + jsonRPCRequest.getMethod();
method = null;
}
}
}
JSONWebServiceActionParameters jsonWebServiceActionParameters =
new JSONWebServiceActionParameters();
jsonWebServiceActionParameters.collectAll(
request, parameterPath, jsonRPCRequest, null);
if (jsonWebServiceActionParameters.getServiceContext() != null) {
ServiceContextThreadLocal.pushServiceContext(
jsonWebServiceActionParameters.getServiceContext());
}
JSONWebServiceActionConfig jsonWebServiceActionConfig =
_findJSONWebServiceAction(
request, path, method, jsonWebServiceActionParameters);
return new JSONWebServiceActionImpl(
jsonWebServiceActionConfig, jsonWebServiceActionParameters,
_jsonWebServiceNaming);
}
@Override
public JSONWebServiceAction getJSONWebServiceAction(
HttpServletRequest request, String path, String method,
Map<String, Object> parameterMap)
throws NoSuchJSONWebServiceException {
JSONWebServiceActionParameters jsonWebServiceActionParameters =
new JSONWebServiceActionParameters();
jsonWebServiceActionParameters.collectAll(
request, null, null, parameterMap);
JSONWebServiceActionConfig jsonWebServiceActionConfig =
_findJSONWebServiceAction(
request, path, method, jsonWebServiceActionParameters);
return new JSONWebServiceActionImpl(
jsonWebServiceActionConfig, jsonWebServiceActionParameters,
_jsonWebServiceNaming);
}
@Override
public JSONWebServiceActionMapping getJSONWebServiceActionMapping(
String signature) {
return _signatureIndexedJSONWebServiceActionConfigs.get(signature);
}
@Override
public List<JSONWebServiceActionMapping> getJSONWebServiceActionMappings(
String contextName) {
List<JSONWebServiceActionConfig> jsonWebServiceActionConfigs =
_contextNameIndexedJSONWebServiceActionConfigs.get(contextName);
if (jsonWebServiceActionConfigs == null) {
return Collections.emptyList();
}
return new ArrayList<JSONWebServiceActionMapping>(
jsonWebServiceActionConfigs);
}
@Override
public int getJSONWebServiceActionsCount(String contextName) {
List<JSONWebServiceActionConfig> jsonWebServiceActionConfigs =
_contextNameIndexedJSONWebServiceActionConfigs.get(contextName);
if (jsonWebServiceActionConfigs == null) {
return 0;
}
return jsonWebServiceActionConfigs.size();
}
@Override
public JSONWebServiceNaming getJSONWebServiceNaming() {
return _jsonWebServiceNaming;
}
@Override
public synchronized void registerJSONWebServiceAction(
String contextName, String contextPath, Class<?> actionClass,
Method actionMethod, String path, String method) {
try {
if (!_addJSONWebServiceActionConfig(
new JSONWebServiceActionConfig(
contextName, contextPath, actionClass, actionMethod,
path, method))) {
if (_log.isDebugEnabled()) {
_log.debug(
"A JSON web service action is already registered at " +
path);
}
}
}
catch (Exception e) {
if (_log.isWarnEnabled()) {
StringBundler sb = new StringBundler(14);
sb.append("Unable to register service method {actionClass=");
sb.append(actionClass);
sb.append(", actionMethod=");
sb.append(actionMethod);
sb.append(", contextName=");
sb.append(contextName);
sb.append(", contextPath=");
sb.append(contextPath);
sb.append(", method=");
sb.append(method);
sb.append(", path=");
sb.append(path);
sb.append("} due to ");
sb.append(e.getMessage());
_log.warn(sb.toString());
}
}
}
@Override
public synchronized void registerJSONWebServiceAction(
String contextName, String contextPath, Object actionObject,
Class<?> actionClass, Method actionMethod, String path, String method) {
try {
if (!_addJSONWebServiceActionConfig(
new JSONWebServiceActionConfig(
contextName, contextPath, actionObject, actionClass,
actionMethod, path, method))) {
if (_log.isWarnEnabled()) {
_log.warn(
"A JSON web service action is already registered at " +
path);
}
}
}
catch (Exception e) {
StringBundler sb = new StringBundler(17);
sb.append("Something went wrong attempting to register service ");
sb.append("method {contextName=");
sb.append(contextName);
sb.append(",contextPath=");
sb.append(contextPath);
sb.append(",actionObject=");
sb.append(actionObject);
sb.append(",actionClass=");
sb.append(actionClass);
sb.append(",actionMethod=");
sb.append(actionMethod);
sb.append(",path=");
sb.append(path);
sb.append(",method=");
sb.append(method);
sb.append("} due to ");
sb.append(e.getMessage());
_log.warn(sb.toString());
}
}
@Override
public int registerService(String contextPath, Object service) {
return registerService(StringPool.BLANK, contextPath, service);
}
@Override
public int registerService(
String contextName, String contextPath, Object service) {
JSONWebServiceRegistrator jsonWebServiceRegistrator =
new DefaultJSONWebServiceRegistrator();
return registerService(
contextName, contextPath, service, jsonWebServiceRegistrator);
}
@Override
public int registerService(
String contextName, String contextPath, Object service,
JSONWebServiceRegistrator jsonWebServiceRegistrator) {
jsonWebServiceRegistrator.processBean(
contextName, contextPath, service);
int count = getJSONWebServiceActionsCount(contextPath);
if (_log.isInfoEnabled()) {
_log.info("Configured " + count + " actions for " + contextPath);
}
return count;
}
@Override
public int registerServletContext(ServletContext servletContext) {
BeanLocator beanLocator = null;
String contextName = servletContext.getServletContextName();
String contextPath = servletContext.getContextPath();
if (contextPath.equals(
PortalContextLoaderListener.getPortalServletContextPath()) ||
contextPath.isEmpty()) {
beanLocator = PortalBeanLocatorUtil.getBeanLocator();
}
else {
beanLocator = PortletBeanLocatorUtil.getBeanLocator(contextName);
}
if (beanLocator == null) {
if (_log.isInfoEnabled()) {
_log.info("Bean locator not available for " + contextPath);
}
return -1;
}
DefaultJSONWebServiceRegistrator defaultJSONWebServiceRegistrator =
new DefaultJSONWebServiceRegistrator();
defaultJSONWebServiceRegistrator.processAllBeans(
contextName, contextPath, beanLocator);
int count = getJSONWebServiceActionsCount(contextPath);
if (_log.isInfoEnabled()) {
_log.info("Configured " + count + " actions for " + contextPath);
}
return count;
}
@Override
public synchronized int unregisterJSONWebServiceActions(
Object actionObject) {
int count = 0;
for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
_signatureIndexedJSONWebServiceActionConfigs.values()) {
if (actionObject.equals(
jsonWebServiceActionConfig.getActionObject()) &&
_removeJSONWebServiceActionConfig(jsonWebServiceActionConfig)) {
count++;
}
}
return count;
}
@Override
public synchronized int unregisterJSONWebServiceActions(
String contextPath) {
int count = 0;
for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
_signatureIndexedJSONWebServiceActionConfigs.values()) {
if (contextPath.equals(
jsonWebServiceActionConfig.getContextPath()) &&
_removeJSONWebServiceActionConfig(jsonWebServiceActionConfig)) {
count++;
}
}
return count;
}
@Override
public int unregisterServletContext(ServletContext servletContext) {
String contextPath = servletContext.getContextPath();
return unregisterJSONWebServiceActions(contextPath);
}
private boolean _addJSONWebServiceActionConfig(
JSONWebServiceActionConfig jsonWebServiceActionConfig) {
JSONWebServiceActionConfig oldJSONWebServiceActionConfig =
_signatureIndexedJSONWebServiceActionConfigs.putIfAbsent(
jsonWebServiceActionConfig.getSignature(),
jsonWebServiceActionConfig);
if (oldJSONWebServiceActionConfig != null) {
return false;
}
String contextName = jsonWebServiceActionConfig.getContextName();
List<JSONWebServiceActionConfig> jsonWebServiceActionConfigs =
_contextNameIndexedJSONWebServiceActionConfigs.get(contextName);
if (jsonWebServiceActionConfigs == null) {
jsonWebServiceActionConfigs = new CopyOnWriteArrayList<>();
_contextNameIndexedJSONWebServiceActionConfigs.put(
contextName, jsonWebServiceActionConfigs);
}
jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
jsonWebServiceActionConfigs =
_pathIndexedJSONWebServiceActionConfigs.get(
jsonWebServiceActionConfig.getPath());
if (jsonWebServiceActionConfigs == null) {
jsonWebServiceActionConfigs = new CopyOnWriteArrayList<>();
_pathIndexedJSONWebServiceActionConfigs.put(
jsonWebServiceActionConfig.getPath(),
jsonWebServiceActionConfigs);
}
jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
return true;
}
private int _countMatchedParameters(
String[] parameterNames, MethodParameter[] methodParameters) {
int matched = 0;
for (MethodParameter methodParameter : methodParameters) {
String methodParameterName = methodParameter.getName();
methodParameterName = StringUtil.toLowerCase(methodParameterName);
for (String parameterName : parameterNames) {
if (StringUtil.equalsIgnoreCase(
parameterName, methodParameterName)) {
matched++;
}
}
}
return matched;
}
private JSONWebServiceActionConfig _findJSONWebServiceAction(
HttpServletRequest request, String path, String method,
JSONWebServiceActionParameters jsonWebServiceActionParameters)
throws NoSuchJSONWebServiceException {
String[] paths = _resolvePaths(request, path);
String contextName = paths[0];
path = paths[1];
if (_log.isDebugEnabled()) {
_log.debug(
"Request JSON web service action with path " + path +
" and method " + method + " for " + contextName);
}
String[] parameterNames =
jsonWebServiceActionParameters.getParameterNames();
JSONWebServiceActionConfig jsonWebServiceActionConfig =
_getJSONWebServiceActionConfig(
contextName, path, method, parameterNames);
if (jsonWebServiceActionConfig == null) {
if (jsonWebServiceActionParameters.includeDefaultParameters()) {
parameterNames =
jsonWebServiceActionParameters.getParameterNames();
jsonWebServiceActionConfig = _getJSONWebServiceActionConfig(
contextName, path, method, parameterNames);
}
}
if (jsonWebServiceActionConfig == null) {
throw new NoSuchJSONWebServiceException(
"No JSON web service action with path " + path +
" and method " + method + " for " + contextName);
}
return jsonWebServiceActionConfig;
}
private JSONWebServiceActionConfig _getJSONWebServiceActionConfig(
String contextName, String path, String method,
String[] parameterNames) {
int hint = -1;
int offset = 0;
if (Validator.isNotNull(contextName)) {
String pathPrefix = StringPool.SLASH.concat(contextName).concat(
StringPool.PERIOD);
if (path.startsWith(pathPrefix)) {
offset = pathPrefix.length();
}
}
int dotIndex = path.indexOf(CharPool.PERIOD, offset);
if (dotIndex != -1) {
hint = GetterUtil.getInteger(path.substring(dotIndex + 1), -1);
if (hint != -1) {
path = path.substring(0, dotIndex);
}
}
List<JSONWebServiceActionConfig> jsonWebServiceActionConfigs =
_pathIndexedJSONWebServiceActionConfigs.get(path);
if ((jsonWebServiceActionConfigs == null) ||
jsonWebServiceActionConfigs.isEmpty()) {
if (_log.isDebugEnabled()) {
_log.debug(
"Unable to find JSON web service actions with path " +
path + " for " + contextName);
}
return null;
}
if (_log.isDebugEnabled()) {
_log.debug(
"Found " + jsonWebServiceActionConfigs.size() +
" JSON web service actions with path " + path + " for " +
contextName);
}
jsonWebServiceActionConfigs = new ArrayList<>(
jsonWebServiceActionConfigs);
Collections.sort(jsonWebServiceActionConfigs);
int max = -1;
JSONWebServiceActionConfig matchedJSONWebServiceActionConfig = null;
for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
jsonWebServiceActionConfigs) {
String jsonWebServiceActionConfigMethod =
jsonWebServiceActionConfig.getMethod();
if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
(method != null)) {
if ((jsonWebServiceActionConfigMethod != null) &&
!jsonWebServiceActionConfigMethod.equals(method)) {
continue;
}
}
MethodParameter[] jsonWebServiceActionConfigMethodParameters =
jsonWebServiceActionConfig.getMethodParameters();
int methodParametersCount =
jsonWebServiceActionConfigMethodParameters.length;
if ((hint != -1) && (methodParametersCount != hint)) {
continue;
}
int count = _countMatchedParameters(
parameterNames, jsonWebServiceActionConfigMethodParameters);
if (count > max) {
if ((hint != -1) || (count >= methodParametersCount)) {
max = count;
matchedJSONWebServiceActionConfig =
jsonWebServiceActionConfig;
}
}
}
if (_log.isDebugEnabled()) {
if (matchedJSONWebServiceActionConfig == null) {
_log.debug(
"Unable to match parameters to a JSON web service action " +
"with path " + path + " for " + contextName);
}
else {
_log.debug(
"Matched parameters to a JSON web service action with " +
"path " + path + " for " + contextName);
}
}
return matchedJSONWebServiceActionConfig;
}
private int _getParameterPathIndex(String path) {
int index = path.indexOf(CharPool.SLASH, 1);
if (index != -1) {
index = path.indexOf(CharPool.SLASH, index + 1);
}
return index;
}
private boolean _removeJSONWebServiceActionConfig(
JSONWebServiceActionConfig jsonWebServiceActionConfig) {
if (!_signatureIndexedJSONWebServiceActionConfigs.remove(
jsonWebServiceActionConfig.getSignature(),
jsonWebServiceActionConfig)) {
return false;
}
String contextName = jsonWebServiceActionConfig.getContextName();
List<JSONWebServiceActionConfig> jsonWebServiceActionConfigs =
_contextNameIndexedJSONWebServiceActionConfigs.get(contextName);
jsonWebServiceActionConfigs.remove(jsonWebServiceActionConfig);
if (jsonWebServiceActionConfigs.isEmpty()) {
_contextNameIndexedJSONWebServiceActionConfigs.remove(contextName);
}
jsonWebServiceActionConfigs =
_pathIndexedJSONWebServiceActionConfigs.get(
jsonWebServiceActionConfig.getPath());
jsonWebServiceActionConfigs.remove(jsonWebServiceActionConfig);
if (jsonWebServiceActionConfigs.isEmpty()) {
_pathIndexedJSONWebServiceActionConfigs.remove(
jsonWebServiceActionConfig.getPath());
}
return true;
}
private String[] _resolvePaths(HttpServletRequest request, String path) {
String contextName = null;
int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
if (index != -1) {
index = path.lastIndexOf(CharPool.PERIOD, index);
if (index != -1) {
contextName = path.substring(1, index);
}
}
if (contextName == null) {
ServletContext servletContext = request.getServletContext();
contextName = servletContext.getServletContextName();
if (Validator.isNotNull(contextName)) {
StringBundler sb = new StringBundler(4);
sb.append(StringPool.SLASH);
sb.append(contextName);
sb.append(StringPool.PERIOD);
sb.append(path.substring(1));
path = sb.toString();
}
}
return new String[] {contextName, path};
}
private static final Log _log = LogFactoryUtil.getLog(
JSONWebServiceActionsManagerImpl.class);
private final Map<String, List<JSONWebServiceActionConfig>>
_contextNameIndexedJSONWebServiceActionConfigs =
new ConcurrentHashMap<>();
private final JSONWebServiceNaming _jsonWebServiceNaming =
new JSONWebServiceNaming();
private final Map<String, List<JSONWebServiceActionConfig>>
_pathIndexedJSONWebServiceActionConfigs = new ConcurrentHashMap<>();
private final ConcurrentMap<String, JSONWebServiceActionConfig>
_signatureIndexedJSONWebServiceActionConfigs =
new ConcurrentHashMap<>();
}