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