You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by lq...@apache.org on 2016/06/14 10:57:28 UTC

svn commit: r1748386 - in /qpid/java/trunk/broker-plugins/management-http/src: main/java/org/apache/qpid/server/management/plugin/ main/java/org/apache/qpid/server/management/plugin/servlet/rest/ test/java/org/apache/qpid/server/management/plugin/servl...

Author: lquack
Date: Tue Jun 14 10:57:27 2016
New Revision: 1748386

URL: http://svn.apache.org/viewvc?rev=1748386&view=rev
Log:
QPID-7124: [Java Broker] Improve parsing of REST URLs

Added:
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParser.java
    qpid/java/trunk/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java
Modified:
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerQueryServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueReportServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java
    qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostQueryServlet.java

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java Tue Jun 14 10:57:27 2016
@@ -22,11 +22,14 @@ package org.apache.qpid.server.managemen
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.security.PrivilegedAction;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -204,4 +207,28 @@ public class HttpManagementUtil
     {
         return requestedFilename.replaceAll("[\\P{InBasic_Latin}\\\\:/\\p{Cntrl}]", "");
     }
+
+    public static List<String> getPathInfoElements(final String servletPath, final String pathInfo)
+    {
+        if (pathInfo == null || pathInfo.length() == 0)
+        {
+            return Collections.emptyList();
+        }
+
+        String[] pathInfoElements = pathInfo.substring(1).split("/");
+        for (int i = 0; i < pathInfoElements.length; i++)
+        {
+            try
+            {
+                // double decode to allow slashes in object names. first decoding happens in request.getPathInfo().
+                pathInfoElements[i] = URLDecoder.decode(pathInfoElements[i], StandardCharsets.UTF_8.name());
+            }
+            catch (UnsupportedEncodingException e)
+            {
+                throw new IllegalArgumentException("Servlet at " + servletPath
+                                                   + " could not decode path element: " + pathInfoElements[i], e);
+            }
+        }
+        return Arrays.asList(pathInfoElements);
+    }
 }

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java Tue Jun 14 10:57:27 2016
@@ -22,9 +22,7 @@ package org.apache.qpid.server.managemen
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Method;
-import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -321,29 +319,6 @@ public abstract class AbstractServlet ex
         response.setDateHeader("Expires", 0);
     }
 
-    protected String[] getPathInfoElements(HttpServletRequest request)
-    {
-        String pathInfo = request.getPathInfo();
-        if (pathInfo != null && pathInfo.length() > 0)
-        {
-            String[] pathInfoElements = pathInfo.substring(1).split("/");
-            for (int i = 0; i < pathInfoElements.length; i++)
-            {
-                try
-                {
-                    // double decode to allow slashes in object names. first decoding happens in request.getPathInfo().
-                    pathInfoElements[i] = URLDecoder.decode(pathInfoElements[i], "UTF-8");
-                }
-                catch (UnsupportedEncodingException e)
-                {
-                    throw new IllegalArgumentException("REST servlet " + getServletName() + " could not decode path element: " + pathInfoElements[i], e);
-                }
-            }
-            return pathInfoElements;
-        }
-        return null;
-    }
-
     protected void writeTypedContent(Content content, HttpServletRequest request, HttpServletResponse response) throws IOException
     {
         try(OutputStream os = getOutputStream(request, response))

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerQueryServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerQueryServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerQueryServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerQueryServlet.java Tue Jun 14 10:57:27 2016
@@ -59,10 +59,11 @@ public class BrokerQueryServlet extends
 
     protected String getRequestedCategory(final HttpServletRequest request)
     {
-        String[] pathInfoElements = getPathInfoElements(request);
-        if(pathInfoElements.length == 1)
+        List<String> pathInfoElements =
+                HttpManagementUtil.getPathInfoElements(request.getServletPath(), request.getPathInfo());
+        if (pathInfoElements.size() == 1)
         {
-            return pathInfoElements[0];
+            return pathInfoElements.get(0);
         }
         return null;
     }

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueReportServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueReportServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueReportServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueReportServlet.java Tue Jun 14 10:57:27 2016
@@ -21,11 +21,13 @@
 package org.apache.qpid.server.management.plugin.servlet.rest;
 
 import java.io.IOException;
+import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
 import org.apache.qpid.server.management.plugin.report.ReportRunner;
 import org.apache.qpid.server.model.Queue;
 import org.apache.qpid.server.model.VirtualHost;
@@ -37,11 +39,12 @@ public class QueueReportServlet extends
                                                                                                       IOException,
                                                                                                       ServletException
     {
-        String[] pathInfoElements = getPathInfoElements(request);
-        if(pathInfoElements != null && pathInfoElements.length == 3)
+        List<String> pathInfoElements =
+                HttpManagementUtil.getPathInfoElements(request.getServletPath(), request.getPathInfo());
+        if (pathInfoElements.size() == 3)
         {
-            Queue<?> queue = getQueueFromRequest(request);
-            ReportRunner<?> reportRunner = ReportRunner.createRunner(pathInfoElements[2],request.getParameterMap());
+            Queue<?> queue = getQueueFromRequest(pathInfoElements);
+            ReportRunner<?> reportRunner = ReportRunner.createRunner(pathInfoElements.get(2),request.getParameterMap());
             Object output = reportRunner.runReport(queue);
             response.setContentType(reportRunner.getContentType());
             if(reportRunner.isBinaryReport())
@@ -60,15 +63,14 @@ public class QueueReportServlet extends
 
     }
 
-    private Queue<?> getQueueFromRequest(HttpServletRequest request)
+    private Queue<?> getQueueFromRequest(final List<String> pathInfoElements)
     {
-        String[] pathInfoElements = getPathInfoElements(request);
-        if(pathInfoElements == null || pathInfoElements.length < 2)
+        if (pathInfoElements.size() < 2)
         {
             throw new IllegalArgumentException("Invalid path is specified");
         }
-        String vhostName = pathInfoElements[0];
-        String queueName = pathInfoElements[1];
+        String vhostName = pathInfoElements.get(0);
+        String queueName = pathInfoElements.get(1);
 
         VirtualHost<?> vhost = getBroker().findVirtualHostByName(vhostName);
         if (vhost == null)

Added: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java?rev=1748386&view=auto
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java (added)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java Tue Jun 14 10:57:27 2016
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.Collections;
+import java.util.List;
+
+public class RequestInfo
+{
+    private final RequestType _type;
+    private final List<String> _modelParts;
+    private final String _operationName;
+
+    public RequestInfo(final RequestType type, final List<String> modelParts)
+    {
+        this(type, modelParts, null);
+    }
+
+    public RequestInfo(final RequestType type, final List<String> modelParts, final String operationName)
+    {
+        _type = type;
+        _operationName = operationName;
+        _modelParts = Collections.unmodifiableList(modelParts);
+    }
+
+    public RequestType getType()
+    {
+        return _type;
+    }
+
+    public List<String> getModelParts()
+    {
+        return _modelParts;
+    }
+
+    public String getOperationName()
+    {
+        if (_type != RequestType.OPERATION)
+        {
+            throw new IllegalStateException("Must not call getOperationName on non-Operation RequestInfo");
+        }
+        return _operationName;
+    }
+
+    enum RequestType
+    {
+        OPERATION, MODEL_OBJECT
+    }
+}

Added: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParser.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParser.java?rev=1748386&view=auto
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParser.java (added)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParser.java Tue Jun 14 10:57:27 2016
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class RequestInfoParser
+{
+    private final List<Class<? extends ConfiguredObject>> _hierarchy;
+
+    public RequestInfoParser(final Class<? extends ConfiguredObject>... hierarchy)
+    {
+        _hierarchy = Arrays.asList(hierarchy);
+    }
+
+    public RequestInfo parse(final HttpServletRequest request)
+    {
+        final String servletPath = request.getServletPath();
+        final String pathInfo = (request.getPathInfo() != null ? request.getPathInfo() : "");
+        List<String> parts = HttpManagementUtil.getPathInfoElements(servletPath, pathInfo);
+
+        final String method = request.getMethod();
+        if ("POST".equals(method))
+        {
+            return parsePost(servletPath, pathInfo, parts);
+        }
+        else if ("PUT".equals(method))
+        {
+            return parsePut(servletPath, pathInfo, parts);
+        }
+        else if ("GET".equals(method))
+        {
+            return parseGet(servletPath, pathInfo, parts);
+        }
+        else if ("DELETE".equals(method))
+        {
+            return parseDelete(servletPath, pathInfo, parts);
+        }
+        else
+        {
+            throw new IllegalArgumentException(String.format("Unexpected method type '%s' for path '%s%s'",
+                                               method, servletPath, pathInfo));
+        }
+    }
+
+    private RequestInfo parseDelete(final String servletPath, final String pathInfo, final List<String> parts)
+    {
+        if (parts.size() <= _hierarchy.size())
+        {
+            return new RequestInfo(RequestInfo.RequestType.MODEL_OBJECT, parts);
+        }
+        else
+        {
+            String expectedPath = buildExpectedPath(servletPath, _hierarchy);
+            throw new IllegalArgumentException(String.format("Invalid DELETE path '%s%s'. Expected: '%s'",
+                                                             servletPath, pathInfo,
+                                                             expectedPath));
+        }
+    }
+
+    private RequestInfo parseGet(final String servletPath, final String pathInfo, final List<String> parts)
+    {
+        if (parts.size() <= _hierarchy.size())
+        {
+            return new RequestInfo(RequestInfo.RequestType.MODEL_OBJECT, parts);
+        }
+        else if (parts.size() == _hierarchy.size() + 1)
+        {
+            return new RequestInfo(RequestInfo.RequestType.OPERATION, parts.subList(0, _hierarchy.size()),
+                                   parts.get(parts.size() - 1));
+        }
+        else
+        {
+            String expectedPath = buildExpectedPath(servletPath, _hierarchy);
+            throw new IllegalArgumentException(String.format("Invalid GET path '%s%s'. Expected: '%s[/<operation name>]'",
+                                                             servletPath, pathInfo,
+                                                             expectedPath));
+        }
+    }
+
+    private RequestInfo parsePut(final String servletPath, final String pathInfo, final List<String> parts)
+    {
+        if (parts.size() == _hierarchy.size() || parts.size() == _hierarchy.size() - 1)
+        {
+            return new RequestInfo(RequestInfo.RequestType.MODEL_OBJECT, parts);
+        }
+        else
+        {
+            String expectedPath = buildExpectedPath(servletPath, _hierarchy);
+            throw new IllegalArgumentException(String.format("Invalid PUT path '%s%s'. Expected: '%s'",
+                                                             servletPath, pathInfo,
+                                                             expectedPath));
+        }
+    }
+
+    private RequestInfo parsePost(final String servletPath, final String pathInfo, final List<String> parts)
+    {
+        if (parts.size() == _hierarchy.size() || parts.size() == _hierarchy.size() - 1)
+        {
+            return new RequestInfo(RequestInfo.RequestType.MODEL_OBJECT, parts);
+        }
+        else if (parts.size() == _hierarchy.size() + 1)
+        {
+            return new RequestInfo(RequestInfo.RequestType.OPERATION, parts.subList(0, _hierarchy.size()),
+                                   parts.get(parts.size() - 1));
+        }
+        else
+        {
+            String expectedFullPath = buildExpectedPath(servletPath, _hierarchy);
+            String expectedParentPath = buildExpectedPath(servletPath, _hierarchy.subList(0, _hierarchy.size() - 1));
+
+            throw new IllegalArgumentException(String.format("Invalid POST path '%s%s'. Expected: '%s/<operation name>'"
+                                                             + " or '%s'",
+                                                             servletPath, pathInfo,
+                                                             expectedFullPath, expectedParentPath));
+        }
+    }
+
+    private String buildExpectedPath(final String servletPath, final List<Class<? extends ConfiguredObject>> hierarchy)
+    {
+        StringBuilder expectedPath = new StringBuilder(servletPath);
+        for (Class<? extends ConfiguredObject> part : hierarchy)
+        {
+            expectedPath.append("/<");
+            expectedPath.append(part.getSimpleName().toLowerCase());
+            expectedPath.append(" name>");
+        }
+        return expectedPath.toString();
+    }
+}

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java Tue Jun 14 10:57:27 2016
@@ -98,7 +98,9 @@ public class RestServlet extends Abstrac
 
     private final ConfiguredObjectToMapConverter _objectConverter = new ConfiguredObjectToMapConverter();
     private final boolean _hierarchyInitializationRequired;
+    private volatile RequestInfoParser _requestInfoParser;
 
+    @SuppressWarnings("unused")
     public RestServlet()
     {
         super();
@@ -120,6 +122,7 @@ public class RestServlet extends Abstrac
         {
             doInitialization();
         }
+        _requestInfoParser = new RequestInfoParser(_hierarchy);
         Handler.register();
     }
 
@@ -163,21 +166,9 @@ public class RestServlet extends Abstrac
         }
     }
 
-    protected Collection<ConfiguredObject<?>> getObjects(HttpServletRequest request)
+    private Collection<ConfiguredObject<?>> getObjects(HttpServletRequest request, RequestInfo requestInfo)
     {
-        String[] pathInfoElements = getPathInfoElements(request);
-        List<String> names = new ArrayList<>();
-        if(pathInfoElements != null)
-        {
-            if(pathInfoElements.length > _hierarchy.length)
-            {
-                throw new IllegalArgumentException("Too many entries in path for REST servlet "
-                        + getServletName() + ". Expected hierarchy length: " + _hierarchy.length
-                        + "; Request hierarchy length: " + pathInfoElements.length
-                        + "; Path Elements: " + Arrays.toString(pathInfoElements));
-            }
-            names.addAll(Arrays.asList(pathInfoElements));
-        }
+        List<String> names = requestInfo.getModelParts();
 
         Collection<ConfiguredObject<?>> parents = new ArrayList<>();
         parents.add(getBroker());
@@ -352,13 +343,15 @@ public class RestServlet extends Abstrac
     @Override
     protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
     {
+        RequestInfo requestInfo = _requestInfoParser.parse(request);
+        switch(requestInfo.getType())
         {
-            String[] pathInfoElements = getPathInfoElements(request);
-            if (pathInfoElements != null && pathInfoElements.length == _hierarchy.length + 1)
+            case OPERATION:
             {
-                doOperation(request, response);
+                doOperation(requestInfo, request, response);
+                break;
             }
-            else
+            case MODEL_OBJECT:
             {
                 // TODO - sort special params, everything else should act as a filter
                 String attachmentFilename = request.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM);
@@ -369,9 +362,9 @@ public class RestServlet extends Abstrac
                     setContentDispositionHeaderIfNecessary(response, attachmentFilename);
                 }
 
-                Collection<ConfiguredObject<?>> allObjects = getObjects(request);
+                Collection<ConfiguredObject<?>> allObjects = getObjects(request, requestInfo);
 
-                if (allObjects == null || allObjects.isEmpty() && isSingleObjectRequest(request))
+                if (allObjects == null || allObjects.isEmpty() && isSingleObjectRequest(requestInfo))
                 {
                     sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Not Found");
                     return;
@@ -474,20 +467,25 @@ public class RestServlet extends Abstrac
 
                 boolean sendCachingHeaders = attachmentFilename == null;
                 sendJsonResponse(extractInitialConfig && output.size() == 1 ? output.get(0) : output,
-                        request,
-                        response,
-                        HttpServletResponse.SC_OK,
-                        sendCachingHeaders);
+                                 request,
+                                 response,
+                                 HttpServletResponse.SC_OK,
+                                 sendCachingHeaders);
+                break;
+            }
+            default:
+            {
+                throw new IllegalStateException(String.format("Unexpected request type '%s' for path '%s'", requestInfo.getType(), request.getPathInfo()));
             }
         }
     }
 
-    private boolean isSingleObjectRequest(HttpServletRequest request)
+    private boolean isSingleObjectRequest(final RequestInfo requestInfo)
     {
         if (_hierarchy.length > 0)
         {
-            String[] pathInfoElements = getPathInfoElements(request);
-            return pathInfoElements != null && pathInfoElements.length == _hierarchy.length;
+            List<String> pathInfoElements = requestInfo.getModelParts();
+            return pathInfoElements.size() == _hierarchy.length;
         }
 
         return false;
@@ -539,14 +537,13 @@ public class RestServlet extends Abstrac
     {
         response.setContentType("application/json");
 
-        List<String> names = getParentNamesFromServletPath(request);
-        boolean isFullObjectURL = names.size() == _hierarchy.length;
-        boolean isPostToFullURL = isFullObjectURL && "POST".equalsIgnoreCase(request.getMethod());
-        final String[] pathInfoElements = getPathInfoElements(request);
-        boolean isOperation = pathInfoElements != null && pathInfoElements.length == _hierarchy.length + 1 && isPostToFullURL;
+        RequestInfo requestInfo = _requestInfoParser.parse(request);
+        switch(requestInfo.getType())
         {
-            if(!isOperation)
+            case MODEL_OBJECT:
             {
+                List<String> names = requestInfo.getModelParts();
+                boolean isFullObjectURL = names.size() == _hierarchy.length;
                 Map<String, Object> providedObject = getRequestProvidedObject(request);
                 if (names.isEmpty() && _hierarchy.length == 0)
                 {
@@ -578,7 +575,7 @@ public class RestServlet extends Abstrac
                         response.setStatus(HttpServletResponse.SC_OK);
                         return;
                     }
-                    else if (isPostToFullURL)
+                    else if ("POST".equalsIgnoreCase(request.getMethod()))
                     {
                         sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Object with "
                                                                                                    + (providedObject.containsKey(
@@ -596,20 +593,25 @@ public class RestServlet extends Abstrac
                 }
                 response.setHeader("Location", requestURL.toString());
                 response.setStatus(HttpServletResponse.SC_CREATED);
+                break;
             }
-            else
+            case OPERATION:
+            {
+                doOperation(requestInfo, request, response);
+                break;
+            }
+            default:
             {
-                doOperation(request, response);
+                throw new IllegalStateException(String.format("Unexpected request type '%s' for path '%s'", requestInfo.getType(), request.getPathInfo()));
             }
         }
     }
 
-    private void doOperation(final HttpServletRequest request,
+    private void doOperation(final RequestInfo requestInfo, final HttpServletRequest request,
                              final HttpServletResponse response) throws IOException, ServletException
     {
         ConfiguredObject<?> subject;
-        final List<String> names = getParentNamesFromServletPath(request);
-        final String[] pathInfoElements = getPathInfoElements(request);
+        final List<String> names = requestInfo.getModelParts();
 
         if (names.isEmpty() && _hierarchy.length == 0)
         {
@@ -636,12 +638,12 @@ public class RestServlet extends Abstrac
                                       HttpServletResponse.SC_NOT_FOUND,
                                       getConfiguredClass().getSimpleName()
                                       + " '"
-                                      + pathInfoElements[pathInfoElements.length - 2]
+                                      + names.get(names.size() - 1)
                                       + "' not found.");
                 return;
             }
         }
-        String operationName = pathInfoElements[pathInfoElements.length - 1];
+        String operationName = requestInfo.getOperationName();
         final Map<String, ConfiguredObjectOperation<?>> availableOperations =
                 getBroker().getModel().getTypeRegistry().getOperations(subject.getClass());
         ConfiguredObjectOperation operation = availableOperations.get(operationName);
@@ -865,40 +867,6 @@ public class RestServlet extends Abstrac
         return parents;
     }
 
-    private List<String> getParentNamesFromServletPath(HttpServletRequest request)
-    {
-        List<String> names = new ArrayList<>();
-        String[] pathInfoElements = getPathInfoElements(request);
-        if (pathInfoElements != null)
-        {
-            if (!(pathInfoElements.length == _hierarchy.length ||
-                    (_hierarchy.length > 0 && pathInfoElements.length == _hierarchy.length - 1)))
-            {
-                if(pathInfoElements.length == _hierarchy.length + 1)
-                {
-                    names.addAll(Arrays.asList(pathInfoElements).subList(0,pathInfoElements.length-1));
-                }
-                else
-                {
-                    throw new IllegalArgumentException(
-                            "Either parent path or full object path must be specified on object creation."
-                            + " Full object path must be specified on object update. "
-                            + "Found "
-                            + names
-                            + " of size "
-                            + names.size()
-                            + " expecting "
-                            + _hierarchy.length);
-                }
-            }
-            else
-            {
-                names.addAll(Arrays.asList(pathInfoElements));
-            }
-        }
-        return names;
-    }
-
     private Map<String, Object> getRequestProvidedObject(HttpServletRequest request) throws IOException, ServletException
     {
         Map<String, Object> providedObject;
@@ -1044,22 +1012,22 @@ public class RestServlet extends Abstrac
     @Override
     protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
     {
-        {
-            Collection<ConfiguredObject<?>> allObjects = getObjects(request);
-            if(allObjects != null)
-            {
-                for (ConfiguredObject o : allObjects)
-                {
-                    o.delete();
-                }
+        RequestInfo requestInfo = _requestInfoParser.parse(request);
 
-                sendCachingHeadersOnResponse(response);
-                response.setStatus(HttpServletResponse.SC_OK);
-            }
-            else
+        Collection<ConfiguredObject<?>> allObjects = getObjects(request, requestInfo);
+        if(allObjects != null)
+        {
+            for (ConfiguredObject o : allObjects)
             {
-                sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Not Found");
+                o.delete();
             }
+
+            sendCachingHeadersOnResponse(response);
+            response.setStatus(HttpServletResponse.SC_OK);
+        }
+        else
+        {
+            sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Not Found");
         }
     }
 

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java Tue Jun 14 10:57:27 2016
@@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRes
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
 import org.apache.qpid.server.model.AuthenticationProvider;
 import org.apache.qpid.server.model.Broker;
 import org.apache.qpid.server.model.PreferencesProvider;
@@ -52,14 +53,15 @@ public class UserPreferencesServlet exte
     protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException,
             ServletException
     {
-        String[] pathElements = getPathInfoElements(request);
-        if (pathElements != null && pathElements.length > 1)
+        List<String> pathElements =
+                HttpManagementUtil.getPathInfoElements(request.getServletPath(), request.getPathInfo());
+        if (pathElements.size() > 1)
         {
-            getUserPreferences(pathElements[0], pathElements[1], request, response);
+            getUserPreferences(pathElements.get(0), pathElements.get(1), request, response);
         }
         else
         {
-            getUserList(pathElements, request, response);
+            getUserList(pathElements.toArray(new String[pathElements.size()]), request, response);
         }
     }
 

Modified: qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostQueryServlet.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostQueryServlet.java?rev=1748386&r1=1748385&r2=1748386&view=diff
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostQueryServlet.java (original)
+++ qpid/java/trunk/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostQueryServlet.java Tue Jun 14 10:57:27 2016
@@ -41,12 +41,16 @@ public class VirtualHostQueryServlet ext
     @Override
     protected VirtualHost<?> getParent(final HttpServletRequest request)
     {
-        final String[] path = getPathInfoElements(request);
+        final List<String>
+                path = HttpManagementUtil.getPathInfoElements(request.getServletPath(), request.getPathInfo());
         final Broker<?> broker = HttpManagementUtil.getBroker(request.getServletContext());
-        VirtualHostNode<?> vhn = broker.getChildByName(VirtualHostNode.class, path[0]);
-        if(vhn != null)
+        if (path.size() == 3)
         {
-            return vhn.getChildByName(VirtualHost.class, path[1]);
+            VirtualHostNode<?> vhn = broker.getChildByName(VirtualHostNode.class, path.get(0));
+            if (vhn != null)
+            {
+                return vhn.getChildByName(VirtualHost.class, path.get(1));
+            }
         }
         return null;
     }
@@ -80,10 +84,11 @@ public class VirtualHostQueryServlet ext
 
     protected String getRequestedCategory(final HttpServletRequest request)
     {
-        String[] pathInfoElements = getPathInfoElements(request);
-        if(pathInfoElements.length == 3)
+        List<String> pathInfoElements =
+                HttpManagementUtil.getPathInfoElements(request.getServletPath(), request.getPathInfo());
+        if (pathInfoElements.size() == 3)
         {
-            return pathInfoElements[2];
+            return pathInfoElements.get(2);
         }
         return null;
     }

Added: qpid/java/trunk/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java
URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java?rev=1748386&view=auto
==============================================================================
--- qpid/java/trunk/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java (added)
+++ qpid/java/trunk/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java Tue Jun 14 10:57:27 2016
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.qpid.server.management.plugin.servlet.rest;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class RequestInfoParserTest extends QpidTestCase
+{
+    private HttpServletRequest _request = mock(HttpServletRequest.class);
+
+    public void testGetNoHierarchy()
+    {
+        RequestInfoParser parser = new RequestInfoParser();
+
+        configureRequest("GET", "servletPath", null);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Collections.emptyList(), info.getModelParts());
+    }
+
+    public void testGetWithHierarchy()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        final String pathInfo = "/" + vhnName + "/" + vhName;
+
+        configureRequest("GET", "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+    public void testGetParent()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+        final String vhnName = "testVHNName";
+        configureRequest("GET", "servletPath", "/" + vhnName);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
+    }
+
+    public void testGetTooManyParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class);
+
+        try
+        {
+            configureRequest("GET", "servletPath", "/testVHNName/testOp/invalidAdditionalPart");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testGetOperation()
+    {
+        doOperationTest("GET");
+    }
+
+    public void testPostToParent()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+
+        final String vhnName = "testVHNName";
+        final String pathInfo = "/" + vhnName;
+
+        configureRequest("POST", "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
+    }
+
+
+    public void testPostToObject()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        final String pathInfo = "/" + vhnName + "/" + vhName;
+
+        configureRequest("POST", "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+    public void testPostTooFewParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        try
+        {
+            configureRequest("POST", "servletPath", "/testVHNName");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testPostTooManyParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        try
+        {
+            configureRequest("POST", "servletPath", "/testVHNName/testVNName/testQueue/testOp/testUnknown");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testPostOperation()
+    {
+        doOperationTest("POST");
+    }
+
+    public void testPutToObject()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        final String pathInfo = "/" + vhnName + "/" + vhName;
+
+        configureRequest("PUT", "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+    public void testPutToParent()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        configureRequest("PUT", "servletPath", "/" + vhnName + "/" + vhName);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+    public void testPutTooFewParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        try
+        {
+            configureRequest("PUT", "servletPath", "/testVHNName");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testPutTooManyParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        try
+        {
+            configureRequest("PUT", "servletPath", "/testVHNName/testVNName/testQueue/testUnknown");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testDeleteObject()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        final String pathInfo = "/" + vhnName + "/" + vhName;
+
+        configureRequest("DELETE", "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+
+    public void testDeleteParent()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        final String vhnName = "testVHNName";
+        final String vhName = "testVHName";
+        configureRequest("DELETE", "servletPath", "/" + vhnName + "/" + vhName);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
+    }
+
+    public void testDeleteTooManyParts()
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class, VirtualHost.class, Queue.class);
+
+        try
+        {
+            configureRequest("DELETE", "servletPath", "/testVHNName/testVNName/testQueue/testUnknown");
+
+            parser.parse(_request);
+            fail("Expected exception");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // pass
+        }
+    }
+
+    public void testParseWithURLEncodedName() throws UnsupportedEncodingException
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class);
+        final String vhnName = "vhnName/With/slashes?and&other/stuff";
+        final String encodedVHNName = URLEncoder.encode(vhnName, StandardCharsets.UTF_8.name());
+
+        configureRequest("GET", "servletPath", "/" + encodedVHNName);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
+    }
+
+    private void configureRequest(final String method,
+                                  final String servletPath,
+                                  final String pathInfo)
+    {
+        when(_request.getServletPath()).thenReturn(servletPath);
+        when(_request.getPathInfo()).thenReturn(pathInfo);
+        when(_request.getMethod()).thenReturn(method);
+    }
+
+    private void doOperationTest(final String method)
+    {
+        RequestInfoParser parser = new RequestInfoParser(VirtualHostNode.class);
+        final String vhnName = "testVHNName";
+        final String operationName = "testOperation";
+        final String pathInfo = "/" + vhnName + "/" + operationName;
+
+        configureRequest(method, "servletPath", pathInfo);
+
+        RequestInfo info = parser.parse(_request);
+
+        assertEquals("Unexpected request type", RequestInfo.RequestType.OPERATION, info.getType());
+        assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
+        assertEquals("Unexpected operation name", operationName, info.getOperationName());
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org