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.jsonwebservice.JSONWebServiceAction;
018    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
019    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManager;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
023    import com.liferay.portal.kernel.servlet.HttpMethods;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.BinarySearch;
026    import com.liferay.portal.kernel.util.CamelCaseUtil;
027    import com.liferay.portal.kernel.util.CharPool;
028    import com.liferay.portal.kernel.util.ContextPathUtil;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.MethodParameter;
031    import com.liferay.portal.kernel.util.SortedArrayList;
032    import com.liferay.portal.kernel.util.StringPool;
033    import com.liferay.portal.util.PortalUtil;
034    import com.liferay.portal.util.PropsValues;
035    
036    import java.lang.reflect.Method;
037    
038    import java.util.ArrayList;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.Map;
042    import java.util.Set;
043    import java.util.TreeSet;
044    
045    import javax.servlet.ServletContext;
046    import javax.servlet.http.HttpServletRequest;
047    import javax.servlet.http.HttpSession;
048    
049    /**
050     * @author Igor Spasic
051     */
052    @DoPrivileged
053    public class JSONWebServiceActionsManagerImpl
054            implements JSONWebServiceActionsManager {
055    
056            @Override
057            public Set<String> getContextPaths() {
058                    Set<String> contextPaths = new TreeSet<String>();
059    
060                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
061                                    _jsonWebServiceActionConfigs) {
062    
063                            String contextPath = jsonWebServiceActionConfig.getContextPath();
064    
065                            contextPaths.add(contextPath);
066                    }
067    
068                    return contextPaths;
069            }
070    
071            @Override
072            public JSONWebServiceAction getJSONWebServiceAction(
073                    HttpServletRequest request) {
074    
075                    String path = GetterUtil.getString(request.getPathInfo());
076                    String method = GetterUtil.getString(request.getMethod());
077    
078                    String[] paths = _resolvePaths(request, path);
079    
080                    String servletContextPath = paths[0];
081    
082                    path = paths[1];
083    
084                    if (_log.isDebugEnabled()) {
085                            _log.debug(
086                                    "Request JSON web service action with path " + path +
087                                            " and method " + method + " for /" + servletContextPath);
088                    }
089    
090                    String parameterPath = null;
091    
092                    JSONRPCRequest jsonRPCRequest = null;
093    
094                    int parameterPathIndex = _getParameterPathIndex(path);
095    
096                    if (parameterPathIndex != -1) {
097                            parameterPath = path.substring(parameterPathIndex);
098    
099                            path = path.substring(0, parameterPathIndex);
100                    }
101                    else {
102                            if (method.equals(HttpMethods.POST) &&
103                                    !PortalUtil.isMultipartRequest(request)) {
104    
105                                    jsonRPCRequest = JSONRPCRequest.detectJSONRPCRequest(request);
106    
107                                    if (jsonRPCRequest != null) {
108                                            path += StringPool.SLASH + jsonRPCRequest.getMethod();
109    
110                                            method = null;
111                                    }
112                            }
113                    }
114    
115                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
116                            new JSONWebServiceActionParameters();
117    
118                    jsonWebServiceActionParameters.collectAll(
119                            request, parameterPath, jsonRPCRequest, null);
120    
121                    int jsonWebServiceActionConfigIndex =
122                            _getJSONWebServiceActionConfigIndex(
123                                    servletContextPath, path, method,
124                                    jsonWebServiceActionParameters.getParameterNames());
125    
126                    if (jsonWebServiceActionConfigIndex == -1) {
127                            throw new RuntimeException(
128                                    "No JSON web service action associated with path " + path +
129                                            " and method " + method + " for /" + servletContextPath);
130                    }
131    
132                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
133                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
134    
135                    return new JSONWebServiceActionImpl(
136                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
137            }
138    
139            @Override
140            public JSONWebServiceAction getJSONWebServiceAction(
141                    HttpServletRequest request, String path, String method,
142                    Map<String, Object> parameterMap) {
143    
144                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
145                            new JSONWebServiceActionParameters();
146    
147                    jsonWebServiceActionParameters.collectAll(
148                            request, null, null, parameterMap);
149    
150                    String[] parameterNames =
151                            jsonWebServiceActionParameters.getParameterNames();
152    
153                    String[] paths = _resolvePaths(request, path);
154    
155                    String servletContextPath = paths[0];
156    
157                    path = paths[1];
158    
159                    if (_log.isDebugEnabled()) {
160                            _log.debug(
161                                    "Require JSON web service action with path " + path +
162                                            " and method " + method + " for /" + servletContextPath);
163                    }
164    
165                    int jsonWebServiceActionConfigIndex =
166                            _getJSONWebServiceActionConfigIndex(
167                                    servletContextPath, path, method, parameterNames);
168    
169                    if (jsonWebServiceActionConfigIndex == -1) {
170                            throw new RuntimeException(
171                                    "No JSON web service action with path " + path +
172                                            " and method " + method + " for /" + servletContextPath);
173                    }
174    
175                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
176                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
177    
178                    return new JSONWebServiceActionImpl(
179                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
180            }
181    
182            @Override
183            public JSONWebServiceActionMapping getJSONWebServiceActionMapping(
184                    String signature) {
185    
186                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
187                                    _jsonWebServiceActionConfigs) {
188    
189                            if (signature.equals(jsonWebServiceActionConfig.getSignature())) {
190                                    return jsonWebServiceActionConfig;
191                            }
192                    }
193    
194                    return null;
195            }
196    
197            @Override
198            public List<JSONWebServiceActionMapping> getJSONWebServiceActionMappings(
199                    String servletContextPath) {
200    
201                    List<JSONWebServiceActionMapping> jsonWebServiceActionMappings =
202                            new ArrayList<JSONWebServiceActionMapping>(
203                                    _jsonWebServiceActionConfigs.size());
204    
205                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
206                                    _jsonWebServiceActionConfigs) {
207    
208                            String jsonWebServiceServletContextPath =
209                                    jsonWebServiceActionConfig.getContextPath();
210    
211                            if (servletContextPath.equals(jsonWebServiceServletContextPath)) {
212                                    jsonWebServiceActionMappings.add(jsonWebServiceActionConfig);
213                            }
214                    }
215    
216                    return jsonWebServiceActionMappings;
217            }
218    
219            @Override
220            public void registerJSONWebServiceAction(
221                    String servletContextPath, Class<?> actionClass, Method actionMethod,
222                    String path, String method) {
223    
224                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
225                            new JSONWebServiceActionConfig(
226                                    servletContextPath, actionClass, actionMethod, path, method);
227    
228                    _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
229            }
230    
231            @Override
232            public int unregisterJSONWebServiceActions(String servletContextPath) {
233                    int count = 0;
234    
235                    Iterator<JSONWebServiceActionConfig> itr =
236                            _jsonWebServiceActionConfigs.iterator();
237    
238                    while (itr.hasNext()) {
239                            JSONWebServiceActionConfig jsonWebServiceActionConfig = itr.next();
240    
241                            if (servletContextPath.equals(
242                                            jsonWebServiceActionConfig.getContextPath())) {
243    
244                                    itr.remove();
245    
246                                    count++;
247                            }
248                    }
249    
250                    return count;
251            }
252    
253            private int _countMatchedElements(
254                    String[] parameterNames, MethodParameter[] methodParameters) {
255    
256                    int matched = 0;
257    
258                    for (MethodParameter methodParameter : methodParameters) {
259                            String methodParameterName = methodParameter.getName();
260    
261                            methodParameterName = CamelCaseUtil.normalizeCamelCase(
262                                    methodParameterName);
263    
264                            if (ArrayUtil.contains(parameterNames, methodParameterName)) {
265                                    matched++;
266                            }
267                    }
268    
269                    return matched;
270            }
271    
272            private int _getJSONWebServiceActionConfigIndex(
273                    String servletContextPath, String path, String method,
274                    String[] parameterNames) {
275    
276                    int hint = -1;
277    
278                    int dotIndex = path.indexOf(CharPool.PERIOD);
279    
280                    if (dotIndex != -1) {
281                            hint = GetterUtil.getInteger(path.substring(dotIndex + 1));
282    
283                            path = path.substring(0, dotIndex);
284                    }
285    
286                    path = servletContextPath + path;
287    
288                    int firstIndex = _pathBinarySearch.findFirst(path);
289    
290                    if (firstIndex < 0) {
291                            if (_log.isDebugEnabled()) {
292                                    _log.debug(
293                                            "Unable to find JSON web service actions with path " +
294                                                    path + " for /" + servletContextPath);
295                            }
296    
297                            return -1;
298                    }
299    
300                    int lastIndex = _pathBinarySearch.findLast(path, firstIndex);
301    
302                    if (lastIndex < 0) {
303                            lastIndex = firstIndex;
304                    }
305    
306                    int index = -1;
307    
308                    int max = -1;
309    
310                    if (_log.isDebugEnabled()) {
311                            int total = lastIndex - firstIndex + 1;
312    
313                            _log.debug(
314                                    "Found " + total + " JSON web service actions with path " +
315                                            path + " in for /" + servletContextPath);
316                    }
317    
318                    for (int i = firstIndex; i <= lastIndex; i++) {
319                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
320                                    _jsonWebServiceActionConfigs.get(i);
321    
322                            String jsonWebServiceActionConfigMethod =
323                                    jsonWebServiceActionConfig.getMethod();
324    
325                            if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
326                                    (method != null)) {
327    
328                                    if ((jsonWebServiceActionConfigMethod != null) &&
329                                            !jsonWebServiceActionConfigMethod.equals(method)) {
330    
331                                            continue;
332                                    }
333                            }
334    
335                            MethodParameter[] jsonWebServiceActionConfigMethodParameters =
336                                    jsonWebServiceActionConfig.getMethodParameters();
337    
338                            int methodParametersCount =
339                                    jsonWebServiceActionConfigMethodParameters.length;
340    
341                            if ((hint != -1) && (methodParametersCount != hint)) {
342                                    continue;
343                            }
344    
345                            int count = _countMatchedElements(
346                                    parameterNames, jsonWebServiceActionConfigMethodParameters);
347    
348                            if (count > max) {
349                                    if ((hint != -1) || (count >= methodParametersCount)) {
350                                            max = count;
351    
352                                            index = i;
353                                    }
354                            }
355                    }
356    
357                    if (_log.isDebugEnabled()) {
358                            if (index == -1) {
359                                    _log.debug(
360                                            "Unable to match parameters to a JSON web service " +
361                                                    "action with path " + path + " for /" +
362                                                            servletContextPath);
363                            }
364                            else {
365                                    _log.debug(
366                                            "Matched parameters to a JSON web service action with " +
367                                                    "path " + path + " for /" + servletContextPath);
368                            }
369                    }
370    
371                    return index;
372            }
373    
374            private int _getParameterPathIndex(String path) {
375                    int index = path.indexOf(CharPool.SLASH, 1);
376    
377                    if (index != -1) {
378                            index = path.indexOf(CharPool.SLASH, index + 1);
379                    }
380    
381                    return index;
382            }
383    
384            private String[] _resolvePaths(HttpServletRequest request, String path) {
385                    String servletContextPath = null;
386    
387                    int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
388    
389                    if (index != -1) {
390                            index = path.lastIndexOf(CharPool.PERIOD, index);
391    
392                            if (index != -1) {
393                                    servletContextPath = path.substring(0, index);
394    
395                                    path = CharPool.FORWARD_SLASH + path.substring(index + 1);
396                            }
397                    }
398    
399                    if (servletContextPath == null) {
400                            HttpSession session = request.getSession();
401    
402                            ServletContext servletContext = session.getServletContext();
403    
404                            servletContextPath = ContextPathUtil.getContextPath(servletContext);
405                    }
406    
407                    return new String[] {servletContextPath, path};
408            }
409    
410            private static Log _log = LogFactoryUtil.getLog(
411                    JSONWebServiceActionsManagerImpl.class);
412    
413            private SortedArrayList<JSONWebServiceActionConfig>
414                    _jsonWebServiceActionConfigs =
415                            new SortedArrayList<JSONWebServiceActionConfig>();
416            private BinarySearch<String> _pathBinarySearch = new PathBinarySearch();
417    
418            private class PathBinarySearch extends BinarySearch<String> {
419    
420                    @Override
421                    protected int compare(int index, String element) {
422                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
423                                    _jsonWebServiceActionConfigs.get(index);
424    
425                            String fullPath = jsonWebServiceActionConfig.getFullPath();
426    
427                            return fullPath.compareTo(element);
428                    }
429    
430                    @Override
431                    protected int getLastIndex() {
432                            return _jsonWebServiceActionConfigs.size() - 1;
433                    }
434    
435            }
436    
437    }