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