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.bean.BeanLocator;
018    import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
019    import com.liferay.portal.kernel.bean.PortletBeanLocatorUtil;
020    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceAction;
021    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionMapping;
022    import com.liferay.portal.kernel.jsonwebservice.JSONWebServiceActionsManager;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.servlet.HttpMethods;
027    import com.liferay.portal.kernel.util.ArrayUtil;
028    import com.liferay.portal.kernel.util.BinarySearch;
029    import com.liferay.portal.kernel.util.CamelCaseUtil;
030    import com.liferay.portal.kernel.util.CharPool;
031    import com.liferay.portal.kernel.util.ContextPathUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.MethodParameter;
034    import com.liferay.portal.kernel.util.SortedArrayList;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.service.ServiceContextThreadLocal;
037    import com.liferay.portal.spring.context.PortalContextLoaderListener;
038    import com.liferay.portal.util.PortalUtil;
039    import com.liferay.portal.util.PropsValues;
040    
041    import java.lang.reflect.Method;
042    
043    import java.util.ArrayList;
044    import java.util.Iterator;
045    import java.util.List;
046    import java.util.Map;
047    import java.util.Set;
048    import java.util.TreeSet;
049    
050    import javax.servlet.ServletContext;
051    import javax.servlet.http.HttpServletRequest;
052    import javax.servlet.http.HttpSession;
053    
054    /**
055     * @author Igor Spasic
056     */
057    @DoPrivileged
058    public class JSONWebServiceActionsManagerImpl
059            implements JSONWebServiceActionsManager {
060    
061            @Override
062            public Set<String> getContextPaths() {
063                    Set<String> contextPaths = new TreeSet<String>();
064    
065                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
066                                    _jsonWebServiceActionConfigs) {
067    
068                            String contextPath = jsonWebServiceActionConfig.getContextPath();
069    
070                            contextPaths.add(contextPath);
071                    }
072    
073                    return contextPaths;
074            }
075    
076            @Override
077            public JSONWebServiceAction getJSONWebServiceAction(
078                    HttpServletRequest request) {
079    
080                    String path = GetterUtil.getString(request.getPathInfo());
081    
082                    String method = GetterUtil.getString(request.getMethod());
083    
084                    String parameterPath = null;
085    
086                    JSONRPCRequest jsonRPCRequest = null;
087    
088                    int parameterPathIndex = _getParameterPathIndex(path);
089    
090                    if (parameterPathIndex != -1) {
091                            parameterPath = path.substring(parameterPathIndex);
092    
093                            path = path.substring(0, parameterPathIndex);
094                    }
095                    else {
096                            if (method.equals(HttpMethods.POST) &&
097                                    !PortalUtil.isMultipartRequest(request)) {
098    
099                                    jsonRPCRequest = JSONRPCRequest.detectJSONRPCRequest(request);
100    
101                                    if (jsonRPCRequest != null) {
102                                            path += StringPool.SLASH + jsonRPCRequest.getMethod();
103    
104                                            method = null;
105                                    }
106                            }
107                    }
108    
109                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
110                            new JSONWebServiceActionParameters();
111    
112                    jsonWebServiceActionParameters.collectAll(
113                            request, parameterPath, jsonRPCRequest, null);
114    
115                    if (jsonWebServiceActionParameters.getServiceContext() != null) {
116                            ServiceContextThreadLocal.pushServiceContext(
117                                    jsonWebServiceActionParameters.getServiceContext());
118                    }
119    
120                    String[] paths = _resolvePaths(request, path);
121    
122                    String contextPath = paths[0];
123    
124                    path = paths[1];
125    
126                    if (_log.isDebugEnabled()) {
127                            _log.debug(
128                                    "Request JSON web service action with path " + path +
129                                            " and method " + method + " for /" + contextPath);
130                    }
131    
132                    int jsonWebServiceActionConfigIndex =
133                            _getJSONWebServiceActionConfigIndex(
134                                    contextPath, path, method,
135                                    jsonWebServiceActionParameters.getParameterNames());
136    
137                    if (jsonWebServiceActionConfigIndex == -1) {
138                            throw new RuntimeException(
139                                    "No JSON web service action associated with path " + path +
140                                            " and method " + method + " for /" + contextPath);
141                    }
142    
143                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
144                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
145    
146                    return new JSONWebServiceActionImpl(
147                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
148            }
149    
150            @Override
151            public JSONWebServiceAction getJSONWebServiceAction(
152                    HttpServletRequest request, String path, String method,
153                    Map<String, Object> parameterMap) {
154    
155                    JSONWebServiceActionParameters jsonWebServiceActionParameters =
156                            new JSONWebServiceActionParameters();
157    
158                    jsonWebServiceActionParameters.collectAll(
159                            request, null, null, parameterMap);
160    
161                    String[] parameterNames =
162                            jsonWebServiceActionParameters.getParameterNames();
163    
164                    String[] paths = _resolvePaths(request, path);
165    
166                    String contextPath = paths[0];
167    
168                    path = paths[1];
169    
170                    if (_log.isDebugEnabled()) {
171                            _log.debug(
172                                    "Request JSON web service action with path " + path +
173                                            " and method " + method + " for /" + contextPath);
174                    }
175    
176                    int jsonWebServiceActionConfigIndex =
177                            _getJSONWebServiceActionConfigIndex(
178                                    contextPath, path, method, parameterNames);
179    
180                    if (jsonWebServiceActionConfigIndex == -1) {
181                            throw new RuntimeException(
182                                    "No JSON web service action with path " + path +
183                                            " and method " + method + " for /" + contextPath);
184                    }
185    
186                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
187                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
188    
189                    return new JSONWebServiceActionImpl(
190                            jsonWebServiceActionConfig, jsonWebServiceActionParameters);
191            }
192    
193            @Override
194            public JSONWebServiceActionMapping getJSONWebServiceActionMapping(
195                    String signature) {
196    
197                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
198                                    _jsonWebServiceActionConfigs) {
199    
200                            if (signature.equals(jsonWebServiceActionConfig.getSignature())) {
201                                    return jsonWebServiceActionConfig;
202                            }
203                    }
204    
205                    return null;
206            }
207    
208            @Override
209            public List<JSONWebServiceActionMapping> getJSONWebServiceActionMappings(
210                    String contextPath) {
211    
212                    List<JSONWebServiceActionMapping> jsonWebServiceActionMappings =
213                            new ArrayList<JSONWebServiceActionMapping>(
214                                    _jsonWebServiceActionConfigs.size());
215    
216                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
217                                    _jsonWebServiceActionConfigs) {
218    
219                            String jsonWebServiceContextPath =
220                                    jsonWebServiceActionConfig.getContextPath();
221    
222                            if (contextPath.equals(jsonWebServiceContextPath)) {
223                                    jsonWebServiceActionMappings.add(jsonWebServiceActionConfig);
224                            }
225                    }
226    
227                    return jsonWebServiceActionMappings;
228            }
229    
230            @Override
231            public int getJSONWebServiceActionsCount(String contextPath) {
232                    int count = 0;
233    
234                    for (JSONWebServiceActionConfig jsonWebServiceActionConfig :
235                                    _jsonWebServiceActionConfigs) {
236    
237                            if (contextPath.equals(
238                                            jsonWebServiceActionConfig.getContextPath())) {
239    
240                                    count++;
241                            }
242                    }
243    
244                    return count;
245            }
246    
247            @Override
248            public void registerJSONWebServiceAction(
249                    String contextPath, Class<?> actionClass, Method actionMethod,
250                    String path, String method) {
251    
252                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
253                            new JSONWebServiceActionConfig(
254                                    contextPath, actionClass, actionMethod, path, method);
255    
256                    if (_jsonWebServiceActionConfigs.contains(jsonWebServiceActionConfig)) {
257                            if (_log.isDebugEnabled()) {
258                                    _log.debug(
259                                            "A JSON web service action is already registered at " +
260                                                    path);
261                            }
262    
263                            return;
264                    }
265    
266                    _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
267            }
268    
269            @Override
270            public void registerJSONWebServiceAction(
271                    String contextPath, Object actionObject, Class<?> actionClass,
272                    Method actionMethod, String path, String method) {
273    
274                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
275                            new JSONWebServiceActionConfig(
276                                    contextPath, actionObject, actionClass, actionMethod, path,
277                                    method);
278    
279                    if (_jsonWebServiceActionConfigs.contains(jsonWebServiceActionConfig)) {
280                            if (_log.isWarnEnabled()) {
281                                    _log.warn(
282                                            "A JSON web service action is already registered at " +
283                                                    path);
284                            }
285    
286                            return;
287                    }
288    
289                    _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
290            }
291    
292            @Override
293            public int registerServletContext(ServletContext servletContext) {
294                    String contextPath = ContextPathUtil.getContextPath(servletContext);
295    
296                    int count = registerServletContext(contextPath);
297    
298                    if (count < 0) {
299                            count = registerServletContext(
300                                    servletContext.getServletContextName());
301                    }
302    
303                    return count;
304            }
305    
306            @Override
307            public int registerServletContext(String contextPath) {
308                    BeanLocator beanLocator = null;
309    
310                    if (contextPath.equals(
311                                    PortalContextLoaderListener.getPortalServletContextPath()) ||
312                            contextPath.isEmpty()) {
313    
314                            beanLocator = PortalBeanLocatorUtil.getBeanLocator();
315                    }
316                    else {
317                            String contextName = contextPath;
318    
319                            if (contextName.startsWith(StringPool.SLASH)) {
320                                    contextName = contextName.substring(1);
321                            }
322    
323                            beanLocator = PortletBeanLocatorUtil.getBeanLocator(contextName);
324                    }
325    
326                    if (beanLocator == null) {
327                            if (_log.isInfoEnabled()) {
328                                    _log.info("Bean locator not available for " + contextPath);
329                            }
330    
331                            return -1;
332                    }
333    
334                    JSONWebServiceRegistrator jsonWebServiceRegistrator =
335                            new JSONWebServiceRegistrator();
336    
337                    jsonWebServiceRegistrator.processAllBeans(contextPath, beanLocator);
338    
339                    int count = getJSONWebServiceActionsCount(contextPath);
340    
341                    if (_log.isInfoEnabled()) {
342                            _log.info("Configured " + count + " actions for " + contextPath);
343                    }
344    
345                    return count;
346            }
347    
348            @Override
349            public int unregisterJSONWebServiceActions(Object actionObject) {
350                    int count = 0;
351    
352                    Iterator<JSONWebServiceActionConfig> iterator =
353                            _jsonWebServiceActionConfigs.iterator();
354    
355                    while (iterator.hasNext()) {
356                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
357                                    iterator.next();
358    
359                            if (actionObject.equals(
360                                            jsonWebServiceActionConfig.getActionObject())) {
361    
362                                    iterator.remove();
363    
364                                    count++;
365                            }
366                    }
367    
368                    return count;
369            }
370    
371            @Override
372            public int unregisterJSONWebServiceActions(String contextPath) {
373                    int count = 0;
374    
375                    Iterator<JSONWebServiceActionConfig> iterator =
376                            _jsonWebServiceActionConfigs.iterator();
377    
378                    while (iterator.hasNext()) {
379                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
380                                    iterator.next();
381    
382                            if (contextPath.equals(
383                                            jsonWebServiceActionConfig.getContextPath())) {
384    
385                                    iterator.remove();
386    
387                                    count++;
388                            }
389                    }
390    
391                    return count;
392            }
393    
394            @Override
395            public int unregisterServletContext(ServletContext servletContext) {
396                    String contextPath = ContextPathUtil.getContextPath(servletContext);
397    
398                    return unregisterJSONWebServiceActions(contextPath);
399            }
400    
401            private int _countMatchedElements(
402                    String[] parameterNames, MethodParameter[] methodParameters) {
403    
404                    int matched = 0;
405    
406                    for (MethodParameter methodParameter : methodParameters) {
407                            String methodParameterName = methodParameter.getName();
408    
409                            methodParameterName = CamelCaseUtil.normalizeCamelCase(
410                                    methodParameterName);
411    
412                            if (ArrayUtil.contains(parameterNames, methodParameterName)) {
413                                    matched++;
414                            }
415                    }
416    
417                    return matched;
418            }
419    
420            private int _getJSONWebServiceActionConfigIndex(
421                    String contextPath, String path, String method,
422                    String[] parameterNames) {
423    
424                    int hint = -1;
425    
426                    int dotIndex = path.indexOf(CharPool.PERIOD);
427    
428                    if (dotIndex != -1) {
429                            hint = GetterUtil.getInteger(path.substring(dotIndex + 1));
430    
431                            path = path.substring(0, dotIndex);
432                    }
433    
434                    path = contextPath + path;
435    
436                    int firstIndex = _pathBinarySearch.findFirst(path);
437    
438                    if (firstIndex < 0) {
439                            if (_log.isDebugEnabled()) {
440                                    _log.debug(
441                                            "Unable to find JSON web service actions with path " +
442                                                    path + " for /" + contextPath);
443                            }
444    
445                            return -1;
446                    }
447    
448                    int lastIndex = _pathBinarySearch.findLast(path, firstIndex);
449    
450                    if (lastIndex < 0) {
451                            lastIndex = firstIndex;
452                    }
453    
454                    int index = -1;
455    
456                    int max = -1;
457    
458                    if (_log.isDebugEnabled()) {
459                            int total = lastIndex - firstIndex + 1;
460    
461                            _log.debug(
462                                    "Found " + total + " JSON web service actions with path " +
463                                            path + " in for /" + contextPath);
464                    }
465    
466                    for (int i = firstIndex; i <= lastIndex; i++) {
467                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
468                                    _jsonWebServiceActionConfigs.get(i);
469    
470                            String jsonWebServiceActionConfigMethod =
471                                    jsonWebServiceActionConfig.getMethod();
472    
473                            if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
474                                    (method != null)) {
475    
476                                    if ((jsonWebServiceActionConfigMethod != null) &&
477                                            !jsonWebServiceActionConfigMethod.equals(method)) {
478    
479                                            continue;
480                                    }
481                            }
482    
483                            MethodParameter[] jsonWebServiceActionConfigMethodParameters =
484                                    jsonWebServiceActionConfig.getMethodParameters();
485    
486                            int methodParametersCount =
487                                    jsonWebServiceActionConfigMethodParameters.length;
488    
489                            if ((hint != -1) && (methodParametersCount != hint)) {
490                                    continue;
491                            }
492    
493                            int count = _countMatchedElements(
494                                    parameterNames, jsonWebServiceActionConfigMethodParameters);
495    
496                            if (count > max) {
497                                    if ((hint != -1) || (count >= methodParametersCount)) {
498                                            max = count;
499    
500                                            index = i;
501                                    }
502                            }
503                    }
504    
505                    if (_log.isDebugEnabled()) {
506                            if (index == -1) {
507                                    _log.debug(
508                                            "Unable to match parameters to a JSON web service " +
509                                                    "action with path " + path + " for /" +
510                                                            contextPath);
511                            }
512                            else {
513                                    _log.debug(
514                                            "Matched parameters to a JSON web service action with " +
515                                                    "path " + path + " for /" + contextPath);
516                            }
517                    }
518    
519                    return index;
520            }
521    
522            private int _getParameterPathIndex(String path) {
523                    int index = path.indexOf(CharPool.SLASH, 1);
524    
525                    if (index != -1) {
526                            index = path.indexOf(CharPool.SLASH, index + 1);
527                    }
528    
529                    return index;
530            }
531    
532            private String[] _resolvePaths(HttpServletRequest request, String path) {
533                    String contextPath = null;
534    
535                    int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
536    
537                    if (index != -1) {
538                            index = path.lastIndexOf(CharPool.PERIOD, index);
539    
540                            if (index != -1) {
541                                    contextPath = path.substring(0, index);
542    
543                                    path = CharPool.FORWARD_SLASH + path.substring(index + 1);
544                            }
545                    }
546    
547                    if (contextPath == null) {
548                            HttpSession session = request.getSession();
549    
550                            ServletContext servletContext = session.getServletContext();
551    
552                            contextPath = ContextPathUtil.getContextPath(servletContext);
553                    }
554    
555                    return new String[] {contextPath, path};
556            }
557    
558            private static Log _log = LogFactoryUtil.getLog(
559                    JSONWebServiceActionsManagerImpl.class);
560    
561            private SortedArrayList<JSONWebServiceActionConfig>
562                    _jsonWebServiceActionConfigs =
563                            new SortedArrayList<JSONWebServiceActionConfig>();
564            private BinarySearch<String> _pathBinarySearch = new PathBinarySearch();
565    
566            private class PathBinarySearch extends BinarySearch<String> {
567    
568                    @Override
569                    protected int compare(int index, String element) {
570                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
571                                    _jsonWebServiceActionConfigs.get(index);
572    
573                            String fullPath = jsonWebServiceActionConfig.getFullPath();
574    
575                            return fullPath.compareTo(element);
576                    }
577    
578                    @Override
579                    protected int getLastIndex() {
580                            return _jsonWebServiceActionConfigs.size() - 1;
581                    }
582    
583            }
584    
585    }