You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by mt...@apache.org on 2019/10/25 21:49:35 UTC

svn commit: r1868963 - in /ofbiz/ofbiz-framework/trunk: ./ framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/

Author: mthl
Date: Fri Oct 25 21:49:35 2019
New Revision: 1868963

URL: http://svn.apache.org/viewvc?rev=1868963&view=rev
Log:
Implemented: Handle URI templates in request maps
(OFBIZ-11007)

It is now possible to use segmented paths by using URI templates like
‘/foo/bar/{baz}’ in the ‘uri’ attribute of <request-map> elements.

Thanks: Artemiy Rozovyk for your contribution.

Modified:
    ofbiz/ofbiz-framework/trunk/build.gradle
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlServlet.java
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java

Modified: ofbiz/ofbiz-framework/trunk/build.gradle
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/build.gradle?rev=1868963&r1=1868962&r2=1868963&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/build.gradle (original)
+++ ofbiz/ofbiz-framework/trunk/build.gradle Fri Oct 25 21:49:35 2019
@@ -307,7 +307,7 @@ checkstyle {
     // the sum of errors that were present before introducing the
     // ‘checkstyle’ tool present in the framework and in the official
     // plugins.
-    maxErrors = 37915
+    maxErrors = 37880
     // Currently there are a lot of errors so we need to temporarily
     // hide them to avoid polluting the terminal output.
     showViolations = false

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlServlet.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlServlet.java?rev=1868963&r1=1868962&r2=1868963&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlServlet.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlServlet.java Fri Oct 25 21:49:35 2019
@@ -77,15 +77,35 @@ public class ControlServlet extends Http
     }
 
     @Override
-    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-        doGet(request, response);
+    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handle(req, resp);
+    }
+
+    @Override
+    public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handle(req, resp);
+    }
+
+    @Override
+    public void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handle(req, resp);
+    }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handle(req, resp);
     }
 
     /**
-     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     * Invokes {@link RequestHandler#doRequest} with error handling.
+     *
+     * @param req  an {@link HttpServletRequest} object that contains the request
+     *             the client has made of the servlet
+     * @param resp  an {@link HttpServletResponse} object that contains the response
+     *              the servlet sends to the client
+     * @throws IOException if an output error is detected when trying to write on the response.
      */
-    @Override
-    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+    public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
         long requestStartTime = System.currentTimeMillis();
         HttpSession session = request.getSession();
 

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java?rev=1868963&r1=1868962&r2=1868963&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java Fri Oct 25 21:49:35 2019
@@ -41,7 +41,9 @@ import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.ws.rs.core.MultivaluedHashMap;
 
+import org.apache.cxf.jaxrs.model.URITemplate;
 import org.apache.ofbiz.base.location.FlexibleLocation;
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.SSLUtil;
@@ -183,7 +185,7 @@ public class RequestHandler {
     }
 
     /**
-     * Find a collection of request maps in {@code ccfg} matching {@code req}.
+     * Finds a collection of request maps in {@code ccfg} matching {@code req}.
      * Otherwise fall back to matching the {@code defaultReq} field in {@code ccfg}.
      *
      * @param ccfg The controller containing the current configuration
@@ -192,20 +194,23 @@ public class RequestHandler {
      */
     static Collection<RequestMap> resolveURI(ControllerConfig ccfg, HttpServletRequest req) {
         Map<String, List<RequestMap>> requestMapMap = ccfg.getRequestMapMap();
-        Map<String, ConfigXMLReader.ViewMap> viewMapMap = ccfg.getViewMapMap();
-        String defaultRequest = ccfg.getDefaultRequest();
-        String path = req.getPathInfo();
-        String requestUri = getRequestUri(path);
-        String viewUri = getOverrideViewUri(path);
-        Collection<RequestMap> rmaps;
-        if (requestMapMap.containsKey(requestUri)
-                // Ensure that overridden view exists.
-                && (viewUri == null || viewMapMap.containsKey(viewUri))) {
-            rmaps = requestMapMap.get(requestUri);
-        } else if (defaultRequest != null) {
-            rmaps = requestMapMap.get(defaultRequest);
-        } else {
-            rmaps = null;
+        Collection<RequestMap> rmaps = resolveTemplateURI(requestMapMap, req);
+        if (rmaps.isEmpty()) {
+            Map<String, ConfigXMLReader.ViewMap> viewMapMap = ccfg.getViewMapMap();
+            String defaultRequest = ccfg.getDefaultRequest();
+            String path = req.getPathInfo();
+            String requestUri = getRequestUri(path);
+            String overrideViewUri = getOverrideViewUri(path);
+            if (requestMapMap.containsKey(requestUri)
+                    // Ensure that overridden view exists.
+                    && (overrideViewUri == null || viewMapMap.containsKey(overrideViewUri))) {
+                rmaps = requestMapMap.get(requestUri);
+                req.setAttribute("overriddenView", overrideViewUri);
+            } else if (defaultRequest != null) {
+                rmaps = requestMapMap.get(defaultRequest);
+            } else {
+                rmaps = null;
+            }
         }
         return rmaps != null ? rmaps : Collections.emptyList();
     }
@@ -234,6 +239,33 @@ public class RequestHandler {
         }
     }
 
+    /**
+     * Finds the request maps matching a segmented path.
+     *
+     * <p>A segmented path can match request maps where the {@code uri} attribute
+     * contains an URI template like in the {@code foo/bar/{baz}} example.
+     *
+     * @param rMapMap  the map associating URIs to a list of request maps corresponding to different HTTP methods
+     * @param request  the HTTP request to match
+     * @return a collection of request maps which might be empty but not {@code null}
+     */
+    private static Collection<RequestMap> resolveTemplateURI(Map<String, List<RequestMap>> rMapMap,
+            HttpServletRequest request) {
+        // Retrieve the request path without the leading '/' character.
+        String path = request.getPathInfo().substring(1);
+        MultivaluedHashMap<String, String> vars = new MultivaluedHashMap<>();
+        for (Map.Entry<String, List<RequestMap>> entry : rMapMap.entrySet()) {
+            URITemplate uriTemplate = URITemplate.createExactTemplate(entry.getKey());
+            // Check if current path the URI template exactly.
+            if (uriTemplate.match(path, vars) && vars.getFirst("FINAL_MATCH_GROUP").equals("/")) {
+                // Set attributes from template variables to be used in context.
+                uriTemplate.getVariables().forEach(var -> request.setAttribute(var, vars.getFirst(var)));
+                return entry.getValue();
+            }
+        }
+        return Collections.emptyList();
+    }
+
     public void doRequest(HttpServletRequest request, HttpServletResponse response, String chain,
             GenericValue userLogin, Delegator delegator) throws RequestHandlerException, RequestHandlerExceptionAllowExternalRequests {
 
@@ -269,7 +301,6 @@ public class RequestHandler {
 
         String path = request.getPathInfo();
         String requestUri = getRequestUri(path);
-        String overrideViewUri = getOverrideViewUri(path);
 
         Collection<RequestMap> rmaps = resolveURI(ccfg, request);
         if (rmaps.isEmpty()) {
@@ -287,8 +318,11 @@ public class RequestHandler {
                 throw new RequestHandlerExceptionAllowExternalRequests();
             }
         }
+        // The "overriddenView" attribute is set by resolveURI when necessary.
+        String overrideViewUri = (String) request.getAttribute("overriddenView");
 
-        String method = request.getMethod();
+        String restMethod = request.getParameter("restMethod");
+        String method = (restMethod != null) ? restMethod : request.getMethod();
         RequestMap requestMap = resolveMethod(method, rmaps).orElseThrow(() -> {
             String msg = UtilProperties.getMessage("WebappUiLabels", "RequestMethodNotMatchConfig",
                     UtilMisc.toList(requestUri, method), UtilHttp.getLocale(request));

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java?rev=1868963&r1=1868962&r2=1868963&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java Fri Oct 25 21:49:35 2019
@@ -28,6 +28,8 @@ import static org.junit.Assert.assertTru
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
 
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -121,6 +123,65 @@ public class RequestHandlerTests {
             assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(bar));
         }
 
+        /**
+         * Checks that segmented URIs are resolved and does not
+         * conflict with overrideViewUri mechanism
+         */
+        @Test
+        public void resolveTemplateURISergmented() {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            RequestMap baz = new RequestMap(dummyElement);
+            reqMaps.putSingle("baz/foo", foo);
+            reqMaps.putSingle("bar", bar);
+            reqMaps.putSingle("baz", baz);
+
+            viewMaps.put("foo", new ViewMap(dummyElement));
+
+            when(req.getPathInfo()).thenReturn("/baz/foo");
+            when(ccfg.getDefaultRequest()).thenReturn("bar");
+            assertThat(RequestHandler.resolveURI(ccfg, req), both(hasItem(foo)).and(not(hasItem(baz))));
+        }
+
+        @Test
+        public void resolveTemplateURIWithVariables() {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo/bar/{var1}/baz/{var2}", foo);
+            reqMaps.putSingle("bar", bar);
+
+            when(req.getPathInfo()).thenReturn("/foo/bar/toto/baz/titi");
+
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(foo));
+            verify(req, times(1)).setAttribute("var1", "toto");
+            verify(req, times(1)).setAttribute("var2", "titi");
+        }
+
+        /**
+         * Currently it is up to the developer to manage URIs with custom
+         * variables that are conflicting with other routes by excluding
+         * them using regular expressions as shown in the test.
+         */
+        @Test
+        public void resolveTemplateURIConflictingRoutes() {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            RequestMap baz = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo/bar", foo);
+            reqMaps.putSingle("foo/qux", bar);
+            reqMaps.putSingle("foo/{var:(?!(bar)|(qux)).*}", baz);
+
+            when(req.getPathInfo()).thenReturn("/foo/bar");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(foo));
+
+            when(req.getPathInfo()).thenReturn("/foo/qux");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(bar));
+
+            when(req.getPathInfo()).thenReturn("/foo/toto");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(baz));
+            verify(req, times(1)).setAttribute("var", "toto");
+        }
+
         @Test
         public void resolveURIBasicOverrideView() throws Exception {
             RequestMap foo = new RequestMap(dummyElement);