001    /**
002     * Copyright (c) 2000-2013 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.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 = servletContext.getServletContextName();
295    
296                    int count = registerServletContext(contextPath);
297    
298                    return count;
299            }
300    
301            @Override
302            public int registerServletContext(String contextPath) {
303                    BeanLocator beanLocator = null;
304    
305                    if (contextPath.equals(
306                                    PortalContextLoaderListener.getPortalServletContextPath()) ||
307                            contextPath.isEmpty()) {
308    
309                            beanLocator = PortalBeanLocatorUtil.getBeanLocator();
310                    }
311                    else {
312                            String contextName = contextPath;
313    
314                            if (!contextPath.startsWith(StringPool.SLASH)) {
315                                    contextPath = StringPool.SLASH.concat(contextPath);
316                            }
317    
318                            beanLocator = PortletBeanLocatorUtil.getBeanLocator(contextName);
319                    }
320    
321                    if (beanLocator == null) {
322                            if (_log.isInfoEnabled()) {
323                                    _log.info("Bean locator not available for " + contextPath);
324                            }
325    
326                            return -1;
327                    }
328    
329                    JSONWebServiceRegistrator jsonWebServiceRegistrator =
330                            new JSONWebServiceRegistrator();
331    
332                    jsonWebServiceRegistrator.processAllBeans(contextPath, beanLocator);
333    
334                    int count = getJSONWebServiceActionsCount(contextPath);
335    
336                    if (_log.isInfoEnabled()) {
337                            _log.info("Configured " + count + " actions for " + contextPath);
338                    }
339    
340                    return count;
341            }
342    
343            @Override
344            public int unregisterJSONWebServiceActions(Object actionObject) {
345                    int count = 0;
346    
347                    Iterator<JSONWebServiceActionConfig> iterator =
348                            _jsonWebServiceActionConfigs.iterator();
349    
350                    while (iterator.hasNext()) {
351                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
352                                    iterator.next();
353    
354                            if (actionObject.equals(
355                                            jsonWebServiceActionConfig.getActionObject())) {
356    
357                                    iterator.remove();
358    
359                                    count++;
360                            }
361                    }
362    
363                    return count;
364            }
365    
366            @Override
367            public int unregisterJSONWebServiceActions(String contextPath) {
368                    int count = 0;
369    
370                    Iterator<JSONWebServiceActionConfig> iterator =
371                            _jsonWebServiceActionConfigs.iterator();
372    
373                    while (iterator.hasNext()) {
374                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
375                                    iterator.next();
376    
377                            if (contextPath.equals(
378                                            jsonWebServiceActionConfig.getContextPath())) {
379    
380                                    iterator.remove();
381    
382                                    count++;
383                            }
384                    }
385    
386                    return count;
387            }
388    
389            @Override
390            public int unregisterServletContext(ServletContext servletContext) {
391                    String contextPath = ContextPathUtil.getContextPath(servletContext);
392    
393                    return unregisterJSONWebServiceActions(contextPath);
394            }
395    
396            private int _countMatchedElements(
397                    String[] parameterNames, MethodParameter[] methodParameters) {
398    
399                    int matched = 0;
400    
401                    for (MethodParameter methodParameter : methodParameters) {
402                            String methodParameterName = methodParameter.getName();
403    
404                            methodParameterName = CamelCaseUtil.normalizeCamelCase(
405                                    methodParameterName);
406    
407                            if (ArrayUtil.contains(parameterNames, methodParameterName)) {
408                                    matched++;
409                            }
410                    }
411    
412                    return matched;
413            }
414    
415            private int _getJSONWebServiceActionConfigIndex(
416                    String contextPath, String path, String method,
417                    String[] parameterNames) {
418    
419                    int hint = -1;
420    
421                    int dotIndex = path.indexOf(CharPool.PERIOD);
422    
423                    if (dotIndex != -1) {
424                            hint = GetterUtil.getInteger(path.substring(dotIndex + 1));
425    
426                            path = path.substring(0, dotIndex);
427                    }
428    
429                    path = contextPath + path;
430    
431                    int firstIndex = _pathBinarySearch.findFirst(path);
432    
433                    if (firstIndex < 0) {
434                            if (_log.isDebugEnabled()) {
435                                    _log.debug(
436                                            "Unable to find JSON web service actions with path " +
437                                                    path + " for /" + contextPath);
438                            }
439    
440                            return -1;
441                    }
442    
443                    int lastIndex = _pathBinarySearch.findLast(path, firstIndex);
444    
445                    if (lastIndex < 0) {
446                            lastIndex = firstIndex;
447                    }
448    
449                    int index = -1;
450    
451                    int max = -1;
452    
453                    if (_log.isDebugEnabled()) {
454                            int total = lastIndex - firstIndex + 1;
455    
456                            _log.debug(
457                                    "Found " + total + " JSON web service actions with path " +
458                                            path + " in for /" + contextPath);
459                    }
460    
461                    for (int i = firstIndex; i <= lastIndex; i++) {
462                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
463                                    _jsonWebServiceActionConfigs.get(i);
464    
465                            String jsonWebServiceActionConfigMethod =
466                                    jsonWebServiceActionConfig.getMethod();
467    
468                            if (PropsValues.JSONWS_WEB_SERVICE_STRICT_HTTP_METHOD &&
469                                    (method != null)) {
470    
471                                    if ((jsonWebServiceActionConfigMethod != null) &&
472                                            !jsonWebServiceActionConfigMethod.equals(method)) {
473    
474                                            continue;
475                                    }
476                            }
477    
478                            MethodParameter[] jsonWebServiceActionConfigMethodParameters =
479                                    jsonWebServiceActionConfig.getMethodParameters();
480    
481                            int methodParametersCount =
482                                    jsonWebServiceActionConfigMethodParameters.length;
483    
484                            if ((hint != -1) && (methodParametersCount != hint)) {
485                                    continue;
486                            }
487    
488                            int count = _countMatchedElements(
489                                    parameterNames, jsonWebServiceActionConfigMethodParameters);
490    
491                            if (count > max) {
492                                    if ((hint != -1) || (count >= methodParametersCount)) {
493                                            max = count;
494    
495                                            index = i;
496                                    }
497                            }
498                    }
499    
500                    if (_log.isDebugEnabled()) {
501                            if (index == -1) {
502                                    _log.debug(
503                                            "Unable to match parameters to a JSON web service " +
504                                                    "action with path " + path + " for /" +
505                                                            contextPath);
506                            }
507                            else {
508                                    _log.debug(
509                                            "Matched parameters to a JSON web service action with " +
510                                                    "path " + path + " for /" + contextPath);
511                            }
512                    }
513    
514                    return index;
515            }
516    
517            private int _getParameterPathIndex(String path) {
518                    int index = path.indexOf(CharPool.SLASH, 1);
519    
520                    if (index != -1) {
521                            index = path.indexOf(CharPool.SLASH, index + 1);
522                    }
523    
524                    return index;
525            }
526    
527            private String[] _resolvePaths(HttpServletRequest request, String path) {
528                    String contextPath = null;
529    
530                    int index = path.indexOf(CharPool.FORWARD_SLASH, 1);
531    
532                    if (index != -1) {
533                            index = path.lastIndexOf(CharPool.PERIOD, index);
534    
535                            if (index != -1) {
536                                    contextPath = path.substring(0, index);
537    
538                                    path = CharPool.FORWARD_SLASH + path.substring(index + 1);
539                            }
540                    }
541    
542                    if (contextPath == null) {
543                            HttpSession session = request.getSession();
544    
545                            ServletContext servletContext = session.getServletContext();
546    
547                            contextPath = ContextPathUtil.getContextPath(servletContext);
548                    }
549    
550                    return new String[] {contextPath, path};
551            }
552    
553            private static Log _log = LogFactoryUtil.getLog(
554                    JSONWebServiceActionsManagerImpl.class);
555    
556            private SortedArrayList<JSONWebServiceActionConfig>
557                    _jsonWebServiceActionConfigs =
558                            new SortedArrayList<JSONWebServiceActionConfig>();
559            private BinarySearch<String> _pathBinarySearch = new PathBinarySearch();
560    
561            private class PathBinarySearch extends BinarySearch<String> {
562    
563                    @Override
564                    protected int compare(int index, String element) {
565                            JSONWebServiceActionConfig jsonWebServiceActionConfig =
566                                    _jsonWebServiceActionConfigs.get(index);
567    
568                            String fullPath = jsonWebServiceActionConfig.getFullPath();
569    
570                            return fullPath.compareTo(element);
571                    }
572    
573                    @Override
574                    protected int getLastIndex() {
575                            return _jsonWebServiceActionConfigs.size() - 1;
576                    }
577    
578            }
579    
580    }