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