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