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                    // Do not use enhanced loop in order to prevent possible
195                    // ConcurrentModificationException
196    
197                    for (int i = 0; i < _jsonWebServiceActionConfigs.size(); i++) {
198                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
199                                    _jsonWebServiceActionConfigs.get(i);
200    
201                            if (contextName.equals(
202                                            jsonWebServiceActionConfig.getContextName())) {
203    
204                                    count++;
205                            }
206                    }
207    
208                    return count;
209            }
210    
211            @Override
212            public JSONWebServiceNaming getJSONWebServiceNaming() {
213                    return _jsonWebServiceNaming;
214            }
215    
216            @Override
217            public void registerJSONWebServiceAction(
218                    String contextName, String contextPath, Class<?> actionClass,
219                    Method actionMethod, String path, String method) {
220    
221                    try {
222                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
223                                    new JSONWebServiceActionConfig(
224                                            contextName, contextPath, actionClass, actionMethod, path,
225                                            method);
226    
227                            if (_jsonWebServiceActionConfigs.contains(
228                                            jsonWebServiceActionConfig)) {
229    
230                                    if (_log.isDebugEnabled()) {
231                                            _log.debug(
232                                                    "A JSON web service action is already registered at " +
233                                                            path);
234                                    }
235    
236                                    return;
237                            }
238    
239                            _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
240                    }
241                    catch (Exception e) {
242                            if (_log.isWarnEnabled()) {
243                                    StringBundler sb = new StringBundler(14);
244    
245                                    sb.append("Unable to register service method {actionClass=");
246                                    sb.append(actionClass);
247                                    sb.append(", actionMethod=");
248                                    sb.append(actionMethod);
249                                    sb.append(", contextName=");
250                                    sb.append(contextName);
251                                    sb.append(", contextPath=");
252                                    sb.append(contextPath);
253                                    sb.append(", method=");
254                                    sb.append(method);
255                                    sb.append(", path=");
256                                    sb.append(path);
257                                    sb.append("} due to ");
258                                    sb.append(e.getMessage());
259    
260                                    _log.warn(sb.toString());
261                            }
262                    }
263            }
264    
265            @Override
266            public void registerJSONWebServiceAction(
267                    String contextName, String contextPath, Object actionObject,
268                    Class<?> actionClass, Method actionMethod, String path, String method) {
269    
270                    try {
271                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
272                                    new JSONWebServiceActionConfig(
273                                            contextName, contextPath, actionObject, actionClass,
274                                            actionMethod, path, method);
275    
276                            if (_jsonWebServiceActionConfigs.contains(
277                                            jsonWebServiceActionConfig)) {
278    
279                                    if (_log.isWarnEnabled()) {
280                                            _log.warn(
281                                                    "A JSON web service action is already registered at " +
282                                                            path);
283                                    }
284    
285                                    return;
286                            }
287    
288                            _jsonWebServiceActionConfigs.add(jsonWebServiceActionConfig);
289                    }
290                    catch (Exception e) {
291                            StringBundler sb = new StringBundler(17);
292    
293                            sb.append("Something went wrong attempting to register service ");
294                            sb.append("method {contextName=");
295                            sb.append(contextName);
296                            sb.append(",contextPath=");
297                            sb.append(contextPath);
298                            sb.append(",actionObject=");
299                            sb.append(actionObject);
300                            sb.append(",actionClass=");
301                            sb.append(actionClass);
302                            sb.append(",actionMethod=");
303                            sb.append(actionMethod);
304                            sb.append(",path=");
305                            sb.append(path);
306                            sb.append(",method=");
307                            sb.append(method);
308                            sb.append("} due to ");
309                            sb.append(e.getMessage());
310    
311                            _log.warn(sb.toString());
312                    }
313            }
314    
315            @Override
316            public int registerService(String contextPath, Object service) {
317                    return registerService(StringPool.BLANK, contextPath, service);
318            }
319    
320            @Override
321            public int registerService(
322                    String contextName, String contextPath, Object service) {
323    
324                    JSONWebServiceRegistrator jsonWebServiceRegistrator =
325                            new JSONWebServiceRegistrator();
326    
327                    jsonWebServiceRegistrator.processBean(
328                            contextName, contextPath, service);
329    
330                    int count = getJSONWebServiceActionsCount(contextPath);
331    
332                    if (_log.isInfoEnabled()) {
333                            _log.info("Configured " + count + " actions for " + contextPath);
334                    }
335    
336                    return count;
337            }
338    
339            @Override
340            public int registerServletContext(ServletContext servletContext) {
341                    BeanLocator beanLocator = null;
342    
343                    String contextName = servletContext.getServletContextName();
344                    String contextPath = servletContext.getContextPath();
345    
346                    if (contextPath.equals(
347                                    PortalContextLoaderListener.getPortalServletContextPath()) ||
348                            contextPath.isEmpty()) {
349    
350                            beanLocator = PortalBeanLocatorUtil.getBeanLocator();
351                    }
352                    else {
353                            beanLocator = PortletBeanLocatorUtil.getBeanLocator(contextName);
354                    }
355    
356                    if (beanLocator == null) {
357                            if (_log.isInfoEnabled()) {
358                                    _log.info("Bean locator not available for " + contextPath);
359                            }
360    
361                            return -1;
362                    }
363    
364                    JSONWebServiceRegistrator jsonWebServiceRegistrator =
365                            new JSONWebServiceRegistrator();
366    
367                    jsonWebServiceRegistrator.processAllBeans(
368                            contextName, contextPath, beanLocator);
369    
370                    int count = getJSONWebServiceActionsCount(contextPath);
371    
372                    if (_log.isInfoEnabled()) {
373                            _log.info("Configured " + count + " actions for " + contextPath);
374                    }
375    
376                    return count;
377            }
378    
379            @Override
380            public int unregisterJSONWebServiceActions(Object actionObject) {
381                    int count = 0;
382    
383                    Iterator<JSONWebServiceActionConfig> iterator =
384                            _jsonWebServiceActionConfigs.iterator();
385    
386                    while (iterator.hasNext()) {
387                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
388                                    iterator.next();
389    
390                            if (actionObject.equals(
391                                            jsonWebServiceActionConfig.getActionObject())) {
392    
393                                    iterator.remove();
394    
395                                    count++;
396                            }
397                    }
398    
399                    return count;
400            }
401    
402            @Override
403            public int unregisterJSONWebServiceActions(String contextPath) {
404                    int count = 0;
405    
406                    Iterator<JSONWebServiceActionConfig> iterator =
407                            _jsonWebServiceActionConfigs.iterator();
408    
409                    while (iterator.hasNext()) {
410                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
411                                    iterator.next();
412    
413                            if (contextPath.equals(
414                                            jsonWebServiceActionConfig.getContextPath())) {
415    
416                                    iterator.remove();
417    
418                                    count++;
419                            }
420                    }
421    
422                    return count;
423            }
424    
425            @Override
426            public int unregisterServletContext(ServletContext servletContext) {
427                    String contextPath = ContextPathUtil.getContextPath(servletContext);
428    
429                    return unregisterJSONWebServiceActions(contextPath);
430            }
431    
432            private int _countMatchedParameters(
433                    String[] parameterNames, MethodParameter[] methodParameters) {
434    
435                    int matched = 0;
436    
437                    for (MethodParameter methodParameter : methodParameters) {
438                            String methodParameterName = methodParameter.getName();
439    
440                            methodParameterName = StringUtil.toLowerCase(methodParameterName);
441    
442                            for (String parameterName : parameterNames) {
443                                    if (StringUtil.equalsIgnoreCase(
444                                                    parameterName, methodParameterName)) {
445    
446                                            matched++;
447                                    }
448                            }
449                    }
450    
451                    return matched;
452            }
453    
454            private JSONWebServiceActionConfig _findJSONWebServiceAction(
455                            HttpServletRequest request, String path, String method,
456                            JSONWebServiceActionParameters jsonWebServiceActionParameters)
457                    throws NoSuchJSONWebServiceException {
458    
459                    String[] paths = _resolvePaths(request, path);
460    
461                    String contextName = paths[0];
462    
463                    path = paths[1];
464    
465                    if (_log.isDebugEnabled()) {
466                            _log.debug(
467                                    "Request JSON web service action with path " + path +
468                                            " and method " + method + " for " + contextName);
469                    }
470    
471                    String[] parameterNames =
472                            jsonWebServiceActionParameters.getParameterNames();
473    
474                    int jsonWebServiceActionConfigIndex =
475                            _getJSONWebServiceActionConfigIndex(
476                                    contextName, path, method, parameterNames);
477    
478                    if (jsonWebServiceActionConfigIndex == -1) {
479                            if (jsonWebServiceActionParameters.includeDefaultParameters()) {
480                                    parameterNames =
481                                            jsonWebServiceActionParameters.getParameterNames();
482    
483                                    jsonWebServiceActionConfigIndex =
484                                            _getJSONWebServiceActionConfigIndex(
485                                                    contextName, path, method, parameterNames);
486                            }
487                    }
488    
489                    if (jsonWebServiceActionConfigIndex == -1) {
490                            throw new NoSuchJSONWebServiceException(
491                                    "No JSON web service action with path " + path +
492                                            " and method " + method + " for " + contextName);
493                    }
494    
495                    JSONWebServiceActionConfig jsonWebServiceActionConfig =
496                            _jsonWebServiceActionConfigs.get(jsonWebServiceActionConfigIndex);
497    
498                    return jsonWebServiceActionConfig;
499            }
500    
501            private int _getJSONWebServiceActionConfigIndex(
502                    String contextName, String path, String method,
503                    String[] parameterNames) {
504    
505                    int hint = -1;
506    
507                    int offset = 0;
508    
509                    if (Validator.isNotNull(contextName)) {
510                            String pathPrefix = StringPool.SLASH.concat(contextName).concat(
511                                    StringPool.PERIOD);
512    
513                            if (path.startsWith(pathPrefix)) {
514                                    offset = pathPrefix.length();
515                            }
516                    }
517    
518                    int dotIndex = path.indexOf(CharPool.PERIOD, offset);
519    
520                    if (dotIndex != -1) {
521                            hint = GetterUtil.getInteger(path.substring(dotIndex + 1), -1);
522    
523                            if (hint != -1) {
524                                    path = path.substring(0, dotIndex);
525                            }
526                    }
527    
528                    int firstIndex = _pathBinarySearch.findFirst(path);
529    
530                    if (firstIndex < 0) {
531                            if (_log.isDebugEnabled()) {
532                                    _log.debug(
533                                            "Unable to find JSON web service actions with path " +
534                                                    path + " for " + contextName);
535                            }
536    
537                            return -1;
538                    }
539    
540                    int lastIndex = _pathBinarySearch.findLast(path, firstIndex);
541    
542                    if (lastIndex < 0) {
543                            lastIndex = firstIndex;
544                    }
545    
546                    int index = -1;
547    
548                    int max = -1;
549    
550                    if (_log.isDebugEnabled()) {
551                            int total = lastIndex - firstIndex + 1;
552    
553                            _log.debug(
554                                    "Found " + total + " JSON web service actions with path " +
555                                            path + " in for " + contextName);
556                    }
557    
558                    for (int i = firstIndex; i <= lastIndex; i++) {
559                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
560                                    _jsonWebServiceActionConfigs.get(i);
561    
562                            String jsonWebServiceActionConfigMethod =
563                                    jsonWebServiceActionConfig.getMethod();
564    
565                            if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
566                                    (method != null)) {
567    
568                                    if ((jsonWebServiceActionConfigMethod != null) &&
569                                            !jsonWebServiceActionConfigMethod.equals(method)) {
570    
571                                            continue;
572                                    }
573                            }
574    
575                            MethodParameter[] jsonWebServiceActionConfigMethodParameters =
576                                    jsonWebServiceActionConfig.getMethodParameters();
577    
578                            int methodParametersCount =
579                                    jsonWebServiceActionConfigMethodParameters.length;
580    
581                            if ((hint != -1) && (methodParametersCount != hint)) {
582                                    continue;
583                            }
584    
585                            int count = _countMatchedParameters(
586                                    parameterNames, jsonWebServiceActionConfigMethodParameters);
587    
588                            if (count > max) {
589                                    if ((hint != -1) || (count >= methodParametersCount)) {
590                                            max = count;
591    
592                                            index = i;
593                                    }
594                            }
595                    }
596    
597                    if (_log.isDebugEnabled()) {
598                            if (index == -1) {
599                                    _log.debug(
600                                            "Unable to match parameters to a JSON web service " +
601                                                    "action with path " + path + " for " + contextName);
602                            }
603                            else {
604                                    _log.debug(
605                                            "Matched parameters to a JSON web service action with " +
606                                                    "path " + path + " for " + contextName);
607                            }
608                    }
609    
610                    return index;
611            }
612    
613            private int _getParameterPathIndex(String path) {
614                    int index = path.indexOf(CharPool.SLASH, 1);
615    
616                    if (index != -1) {
617                            index = path.indexOf(CharPool.SLASH, index + 1);
618                    }
619    
620                    return index;
621            }
622    
623            private String[] _resolvePaths(HttpServletRequest request, String path) {
624                    String contextName = null;
625    
626                    int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
627    
628                    if (index != -1) {
629                            index = path.lastIndexOf(CharPool.PERIOD, index);
630    
631                            if (index != -1) {
632                                    contextName = path.substring(1, index);
633                            }
634                    }
635    
636                    if (contextName == null) {
637                            ServletContext servletContext = request.getServletContext();
638    
639                            contextName = servletContext.getServletContextName();
640    
641                            if (Validator.isNotNull(contextName)) {
642                                    path = StringPool.SLASH.concat(contextName).concat(
643                                            StringPool.PERIOD).concat(path.substring(1));
644                            }
645                    }
646    
647                    return new String[] {contextName, path};
648            }
649    
650            private static Log _log = LogFactoryUtil.getLog(
651                    JSONWebServiceActionsManagerImpl.class);
652    
653            private SortedArrayList<JSONWebServiceActionConfig>
654                    _jsonWebServiceActionConfigs =
655                            new SortedArrayList<JSONWebServiceActionConfig>();
656            private JSONWebServiceNaming _jsonWebServiceNaming =
657                    new JSONWebServiceNaming();
658            private BinarySearch<String> _pathBinarySearch = new PathBinarySearch();
659    
660            private class PathBinarySearch extends BinarySearch<String> {
661    
662                    @Override
663                    protected int compare(int index, String element) {
664                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
665                                    _jsonWebServiceActionConfigs.get(index);
666    
667                            String path = jsonWebServiceActionConfig.getPath();
668    
669                            return path.compareTo(element);
670                    }
671    
672                    @Override
673                    protected int getLastIndex() {
674                            return _jsonWebServiceActionConfigs.size() - 1;
675                    }
676    
677            }
678    
679    }