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    
077                    String method = GetterUtil.getString(request.getMethod());
078    
079                    String parameterPath = null;
080    
081                    JSONRPCRequest jsonRPCRequest = null;
082    
083                    int parameterPathIndex = _getParameterPathIndex(path);
084    
085                    if (parameterPathIndex != -1) {
086                            parameterPath = path.substring(parameterPathIndex);
087    
088                            path = path.substring(0, parameterPathIndex);
089                    }
090                    else {
091                            if (method.equals(HttpMethods.POST) &&
092                                    !PortalUtil.isMultipartRequest(request)) {
093    
094                                    jsonRPCRequest = JSONRPCRequest.detectJSONRPCRequest(request);
095    
096                                    if (jsonRPCRequest != null) {
097                                            path += StringPool.SLASH + jsonRPCRequest.getMethod();
098    
099                                            method = null;
100                                    }
101                            }
102                    }
103    
104                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
105                            new JSONWebServiceActionParameters();
106    
107                    jsonWebServiceActionParameters.collectAll(
108                            request, parameterPath, jsonRPCRequest, null);
109    
110                    String[] paths = _resolvePaths(request, path);
111    
112                    String contextPath = paths[0];
113    
114                    path = paths[1];
115    
116                    if (_log.isDebugEnabled()) {
117                            _log.debug(
118                                    "Request JSON web service action with path " + path +
119                                            " and method " + method + " for /" + contextPath);
120                    }
121    
122                    int jsonWebServiceActionConfigIndex =
123                            _getJSONWebServiceActionConfigIndex(
124                                    contextPath, path, method,
125                                    jsonWebServiceActionParameters.getParameterNames());
126    
127                    if (jsonWebServiceActionConfigIndex == -1) {
128                            throw new RuntimeException(
129                                    "No JSON web service action associated with path " + path +
130                                            " and method " + method + " for /" + contextPath);
131                    }
132    
133                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
134                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
135    
136                    return new JSONWebServiceActionImpl(
137                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
138            }
139    
140            @Override
141            public JSONWebServiceAction getJSONWebServiceAction(
142                    HttpServletRequest request, String path, String method,
143                    Map<String, Object> parameterMap) {
144    
145                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
146                            new JSONWebServiceActionParameters();
147    
148                    jsonWebServiceActionParameters.collectAll(
149                            request, null, null, parameterMap);
150    
151                    String[] parameterNames =
152                            jsonWebServiceActionParameters.getParameterNames();
153    
154                    String[] paths = _resolvePaths(request, path);
155    
156                    String contextPath = paths[0];
157    
158                    path = paths[1];
159    
160                    if (_log.isDebugEnabled()) {
161                            _log.debug(
162                                    "Request JSON web service action with path " + path +
163                                            " and method " + method + " for /" + contextPath);
164                    }
165    
166                    int jsonWebServiceActionConfigIndex =
167                            _getJSONWebServiceActionConfigIndex(
168                                    contextPath, path, method, parameterNames);
169    
170                    if (jsonWebServiceActionConfigIndex == -1) {
171                            throw new RuntimeException(
172                                    "No JSON web service action with path " + path +
173                                            " and method " + method + " for /" + contextPath);
174                    }
175    
176                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
177                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
178    
179                    return new JSONWebServiceActionImpl(
180                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
181            }
182    
183            @Override
184            public JSONWebServiceActionMapping getJSONWebServiceActionMapping(
185                    String signature) {
186    
187                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
188                                    _jsonWebServiceActionConfigs) {
189    
190                            if (signature.equals(jsonWebServiceActionConfig.getSignature())) {
191                                    return jsonWebServiceActionConfig;
192                            }
193                    }
194    
195                    return null;
196            }
197    
198            @Override
199            public List<JSONWebServiceActionMapping> getJSONWebServiceActionMappings(
200                    String contextPath) {
201    
202                    List<JSONWebServiceActionMapping> jsonWebServiceActionMappings =
203                            new ArrayList<JSONWebServiceActionMapping>(
204                                    _jsonWebServiceActionConfigs.size());
205    
206                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
207                                    _jsonWebServiceActionConfigs) {
208    
209                            String jsonWebServiceContextPath =
210                                    jsonWebServiceActionConfig.getContextPath();
211    
212                            if (contextPath.equals(jsonWebServiceContextPath)) {
213                                    jsonWebServiceActionMappings.add(jsonWebServiceActionConfig);
214                            }
215                    }
216    
217                    return jsonWebServiceActionMappings;
218            }
219    
220            @Override
221            public int getJSONWebServiceActionsCount(String contextPath) {
222                    int count = 0;
223    
224                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
225                                    _jsonWebServiceActionConfigs) {
226    
227                            if (contextPath.equals(
228                                            jsonWebServiceActionConfig.getContextPath())) {
229    
230                                    count++;
231                            }
232                    }
233    
234                    return count;
235            }
236    
237            @Override
238            public void registerJSONWebServiceAction(
239                    String contextPath, Class<?> actionClass, Method actionMethod,
240                    String path, String method) {
241    
242                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
243                            new JSONWebServiceActionConfig(
244                                    contextPath, actionClass, actionMethod, path, method);
245    
246                    if (_jsonWebServiceActionConfigs.contains(jsonWebServiceActionConfig)) {
247                            if (_log.isDebugEnabled()) {
248                                    _log.debug(
249                                            "A JSON web service action is already registered at " +
250                                                    path);
251                            }
252    
253                            return;
254                    }
255    
256                    _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
257            }
258    
259            @Override
260            public void registerJSONWebServiceAction(
261                    String contextPath, Object actionObject, Class<?> actionClass,
262                    Method actionMethod, String path, String method) {
263    
264                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
265                            new JSONWebServiceActionConfig(
266                                    contextPath, actionObject, actionClass, actionMethod, path,
267                                    method);
268    
269                    if (_jsonWebServiceActionConfigs.contains(jsonWebServiceActionConfig)) {
270                            if (_log.isDebugEnabled()) {
271                                    _log.debug(
272                                            "A JSON web service action is already registered at " +
273                                                    path);
274                            }
275    
276                            return;
277                    }
278    
279                    _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
280            }
281    
282            @Override
283            public int unregisterJSONWebServiceActions(Object actionObject) {
284                    int count = 0;
285    
286                    Iterator<JSONWebServiceActionConfig> iterator =
287                            _jsonWebServiceActionConfigs.iterator();
288    
289                    while (iterator.hasNext()) {
290                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
291                                    iterator.next();
292    
293                            if (actionObject.equals(
294                                            jsonWebServiceActionConfig.getActionObject())) {
295    
296                                    iterator.remove();
297    
298                                    count++;
299                            }
300                    }
301    
302                    return count;
303            }
304    
305            @Override
306            public int unregisterJSONWebServiceActions(String contextPath) {
307                    int count = 0;
308    
309                    Iterator<JSONWebServiceActionConfig> iterator =
310                            _jsonWebServiceActionConfigs.iterator();
311    
312                    while (iterator.hasNext()) {
313                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
314                                    iterator.next();
315    
316                            if (contextPath.equals(
317                                            jsonWebServiceActionConfig.getContextPath())) {
318    
319                                    iterator.remove();
320    
321                                    count++;
322                            }
323                    }
324    
325                    return count;
326            }
327    
328            private int _countMatchedElements(
329                    String[] parameterNames, MethodParameter[] methodParameters) {
330    
331                    int matched = 0;
332    
333                    for (MethodParameter methodParameter : methodParameters) {
334                            String methodParameterName = methodParameter.getName();
335    
336                            methodParameterName = CamelCaseUtil.normalizeCamelCase(
337                                    methodParameterName);
338    
339                            if (ArrayUtil.contains(parameterNames, methodParameterName)) {
340                                    matched++;
341                            }
342                    }
343    
344                    return matched;
345            }
346    
347            private int _getJSONWebServiceActionConfigIndex(
348                    String contextPath, String path, String method,
349                    String[] parameterNames) {
350    
351                    int hint = -1;
352    
353                    int dotIndex = path.indexOf(CharPool.PERIOD);
354    
355                    if (dotIndex != -1) {
356                            hint = GetterUtil.getInteger(path.substring(dotIndex + 1));
357    
358                            path = path.substring(0, dotIndex);
359                    }
360    
361                    path = contextPath + path;
362    
363                    int firstIndex = _pathBinarySearch.findFirst(path);
364    
365                    if (firstIndex < 0) {
366                            if (_log.isDebugEnabled()) {
367                                    _log.debug(
368                                            "Unable to find JSON web service actions with path " +
369                                                    path + " for /" + contextPath);
370                            }
371    
372                            return -1;
373                    }
374    
375                    int lastIndex = _pathBinarySearch.findLast(path, firstIndex);
376    
377                    if (lastIndex < 0) {
378                            lastIndex = firstIndex;
379                    }
380    
381                    int index = -1;
382    
383                    int max = -1;
384    
385                    if (_log.isDebugEnabled()) {
386                            int total = lastIndex - firstIndex + 1;
387    
388                            _log.debug(
389                                    "Found " + total + " JSON web service actions with path " +
390                                            path + " in for /" + contextPath);
391                    }
392    
393                    for (int i = firstIndex; i <= lastIndex; i++) {
394                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
395                                    _jsonWebServiceActionConfigs.get(i);
396    
397                            String jsonWebServiceActionConfigMethod =
398                                    jsonWebServiceActionConfig.getMethod();
399    
400                            if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
401                                    (method != null)) {
402    
403                                    if ((jsonWebServiceActionConfigMethod != null) &&
404                                            !jsonWebServiceActionConfigMethod.equals(method)) {
405    
406                                            continue;
407                                    }
408                            }
409    
410                            MethodParameter[] jsonWebServiceActionConfigMethodParameters =
411                                    jsonWebServiceActionConfig.getMethodParameters();
412    
413                            int methodParametersCount =
414                                    jsonWebServiceActionConfigMethodParameters.length;
415    
416                            if ((hint != -1) && (methodParametersCount != hint)) {
417                                    continue;
418                            }
419    
420                            int count = _countMatchedElements(
421                                    parameterNames, jsonWebServiceActionConfigMethodParameters);
422    
423                            if (count > max) {
424                                    if ((hint != -1) || (count >= methodParametersCount)) {
425                                            max = count;
426    
427                                            index = i;
428                                    }
429                            }
430                    }
431    
432                    if (_log.isDebugEnabled()) {
433                            if (index == -1) {
434                                    _log.debug(
435                                            "Unable to match parameters to a JSON web service " +
436                                                    "action with path " + path + " for /" +
437                                                            contextPath);
438                            }
439                            else {
440                                    _log.debug(
441                                            "Matched parameters to a JSON web service action with " +
442                                                    "path " + path + " for /" + contextPath);
443                            }
444                    }
445    
446                    return index;
447            }
448    
449            private int _getParameterPathIndex(String path) {
450                    int index = path.indexOf(CharPool.SLASH, 1);
451    
452                    if (index != -1) {
453                            index = path.indexOf(CharPool.SLASH, index + 1);
454                    }
455    
456                    return index;
457            }
458    
459            private String[] _resolvePaths(HttpServletRequest request, String path) {
460                    String contextPath = null;
461    
462                    int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
463    
464                    if (index != -1) {
465                            index = path.lastIndexOf(CharPool.PERIOD, index);
466    
467                            if (index != -1) {
468                                    contextPath = path.substring(0, index);
469    
470                                    path = CharPool.FORWARD_SLASH + path.substring(index + 1);
471                            }
472                    }
473    
474                    if (contextPath == null) {
475                            HttpSession session = request.getSession();
476    
477                            ServletContext servletContext = session.getServletContext();
478    
479                            contextPath = ContextPathUtil.getContextPath(servletContext);
480                    }
481    
482                    return new String[] {contextPath, path};
483            }
484    
485            private static Log _log = LogFactoryUtil.getLog(
486                    JSONWebServiceActionsManagerImpl.class);
487    
488            private SortedArrayList<JSONWebServiceActionConfig>
489                    _jsonWebServiceActionConfigs =
490                            new SortedArrayList<JSONWebServiceActionConfig>();
491            private BinarySearch<String> _pathBinarySearch = new PathBinarySearch();
492    
493            private class PathBinarySearch extends BinarySearch<String> {
494    
495                    @Override
496                    protected int compare(int index, String element) {
497                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
498                                    _jsonWebServiceActionConfigs.get(index);
499    
500                            String fullPath = jsonWebServiceActionConfig.getFullPath();
501    
502                            return fullPath.compareTo(element);
503                    }
504    
505                    @Override
506                    protected int getLastIndex() {
507                            return _jsonWebServiceActionConfigs.size() - 1;
508                    }
509    
510            }
511    
512    }