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