Author: shijh
Date: Wed Aug 15 11:45:45 2018
New Revision: 1838081

URL: http://svn.apache.org/viewvc?rev=1838081&view=rev
Log:
Implemented: Add method attribute to request-map to controll a uri can be 
called GET or POST only
OFBIZ-10438

Thanks: Mathieu Lirzin for the contribution.

Added:
    
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
   (with props)
    
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
   (with props)
    
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/
    
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
   (with props)
    
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
   (with props)
    
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
   (with props)
    
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
   (with props)
Modified:
    ofbiz/ofbiz-framework/trunk/framework/webapp/config/WebappUiLabels.xml
    ofbiz/ofbiz-framework/trunk/framework/webapp/dtd/site-conf.xsd
    
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java
    
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

Added: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java?rev=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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.ofbiz.base.util.collections;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * MultivaluedMap Context
+ *
+ * A MapContext which handles multiple values for the same key.
+ */
+public class MultivaluedMapContext<K, V> extends MapContext<K, List<V>> {
+
+    public static final String module = MultivaluedMapContext.class.getName();
+
+    /**
+     * Create a multi-value map initialized with one context
+     */
+    public MultivaluedMapContext() {
+        push();
+    }
+
+    /**
+     * Associate {@code key} with the single value {@code value}.
+     * If other values are already associated with {@code key} then override 
them.
+     *
+     * @param key the key to associate {@code value} with
+     * @param value the value to add to the context
+     */
+    public void putSingle(K key, V value) {
+        List<V> box = new LinkedList<>();
+        box.add(value);
+        put(key, box);
+    }
+
+    /**
+     * Associate {@code key} with the single value {@code value}.
+     * If other values are already associated with {@code key},
+     * then add {@code value} to them.
+     *
+     * @param key the key to associate {@code value} with
+     * @param value the value to add to the context
+     */
+    public void add(K key, V value) {
+        List<V> cur = contexts.getFirst().get(key);
+        if (cur == null) {
+            cur = new LinkedList<>();
+            /* if this method is called after a context switch, copy the 
previous values
+               in current context to not mask them. */
+            List<V> old = get(key);
+            if (old != null) {
+                cur.addAll(old);
+            }
+        }
+        cur.add(value);
+        put(key, cur);
+    }
+
+    /**
+     * Get the first value contained in the list of values associated with 
{@code key}.
+     *
+     * @param key a candidate key
+     * @return the first value associated with {@code key} or null if no value
+     * is associated with it.
+     */
+    public V getFirst(Object key) {
+        List<V> res = get(key);
+        return res == null ? null : res.get(0);
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java?rev=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * 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.ofbiz.base.util.collections;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+// Adapter which allows viewing a multi-value map as a single-value map.
+public class MultivaluedMapContextAdapter<K, V> implements Map<K, V> {
+    private MultivaluedMapContext<K, V> adaptee;
+
+    public MultivaluedMapContextAdapter(MultivaluedMapContext<K, V> adaptee) {
+        this.adaptee = adaptee;
+    }
+
+    @Override
+    public int size() {
+        return adaptee.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return adaptee.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return adaptee.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return adaptee.values().stream()
+                .map(l -> l.get(0))
+                .anyMatch(value::equals);
+    }
+
+    @Override
+    public V get(Object key) {
+        return adaptee.getFirst(key);
+    }
+
+    @Override
+    public V put(K key, V value) {
+        V prev = get(key);
+        adaptee.putSingle(key, value);
+        return prev;
+    }
+
+    @Override
+    public V remove(Object key) {
+        V prev = get(key);
+        adaptee.remove(key);
+        return prev;
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        m.forEach(adaptee::putSingle);
+    }
+
+    @Override
+    public void clear() {
+        adaptee.clear();
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return adaptee.keySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return adaptee.values().stream()
+                .map(l -> l.get(0))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return adaptee.keySet().stream()
+                .collect(Collectors.toMap(k -> k, k -> get(k)))
+                .entrySet();
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/collections/MultivaluedMapContextAdapter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java?rev=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.ofbiz.base.collections;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContextAdapter;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MultivaluedMapContextAdapterTests {
+    private MultivaluedMapContext<String, Integer> adaptee;
+    private MultivaluedMapContextAdapter<String, Integer> adapter;
+
+    @Before
+    public void setUp() throws Exception {
+        adaptee = new MultivaluedMapContext<>();
+        adaptee.add("foo", 0);
+        adaptee.add("foo", 1);
+        adaptee.add("foo", 2);
+        adaptee.add("bar", 3);
+        adapter = new MultivaluedMapContextAdapter<>(adaptee);
+    }
+
+    @Test
+    public void containsValueBasic() {
+        assertTrue(adapter.containsValue(0));
+        assertFalse(adapter.containsValue(1));
+        assertFalse(adapter.containsValue(2));
+        assertTrue(adapter.containsValue(3));
+    }
+
+    @Test
+    public void valuesBasic() {
+        assertThat(adapter.values(), hasItems(0, 3));
+        assertThat(adapter.values().size(), is(2));
+    }
+
+    @Test
+    public void entrySetBasic() {
+        HashMap<String, Integer> expected = new HashMap<>();
+        expected.put("foo", 0);
+        expected.put("bar", 3);
+        assertEquals(expected.entrySet(), adapter.entrySet());
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextAdapterTests.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java?rev=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.ofbiz.base.collections;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MultivaluedMapContextTests {
+    private MultivaluedMapContext<String, Integer> m;
+
+    @Before
+    public void setUp() throws Exception {
+        m = new MultivaluedMapContext<>();
+    }
+
+    @Test
+    public void getEmpty() {
+        assertThat(m.get("foo"), is(nullValue()));
+    }
+
+    @Test
+    public void putSingleBasic() {
+        m.putSingle("foo", 0);
+        assertThat(m.get("foo"), hasItem(0));
+        m.putSingle("foo", 1);
+        assertThat(m.get("foo"), both(hasItem(1)).and(not(hasItem(0))));
+    }
+
+    @Test
+    public void addBasic() {
+        m.add("foo", 0);
+        assertThat(m.get("foo"), hasItem(0));
+        m.add("foo", 1);
+        assertThat(m.get("foo"), hasItems(0, 1));
+    }
+
+    @Test
+    public void addWithPreviousContext() {
+        m.add("foo", 0);
+        m.push();
+        assertThat(m.get("foo"), hasItem(0));
+        m.add("foo", 1);
+        assertThat(m.get("foo"), hasItems(0, 1));
+    }
+
+    @Test
+    public void getFirstBasic() {
+        m.add("foo", 0);
+        m.add("foo", 1);
+        assertThat(m.getFirst("foo"), is(0));
+    }
+
+    @Test
+    public void getFirstEmpty() {
+        assertThat(m.getFirst("foo"), is(nullValue()));
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/base/src/test/java/org/apache/ofbiz/base/collections/MultivaluedMapContextTests.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/config/WebappUiLabels.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/config/WebappUiLabels.xml?rev=1838081&r1=1838080&r2=1838081&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/config/WebappUiLabels.xml 
(original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/config/WebappUiLabels.xml Wed 
Aug 15 11:45:45 2018
@@ -339,4 +339,8 @@
         <value xml:lang="zh">没有完成事件</value>
         <value xml:lang="zh-TW">沒有完成事件</value>
     </property>
+    <property key="RequestMethodNotMatchConfig">
+        <value xml:lang="en">[{0}] cannot be called by [{1}] method.</value>
+        <value xml:lang="zh">[{0}]不能用[{1}]方法请求。</value>
+    </property>
 </resource>

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/dtd/site-conf.xsd
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/dtd/site-conf.xsd?rev=1838081&r1=1838080&r2=1838081&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/dtd/site-conf.xsd (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/dtd/site-conf.xsd Wed Aug 15 
11:45:45 2018
@@ -216,6 +216,24 @@ under the License.
                 </xs:documentation>
             </xs:annotation>
         </xs:attribute>
+        <xs:attribute name="method" use="optional" default="all">
+            <xs:annotation>
+                <xs:documentation>
+                    The HTTP of this request. This will be the HTTP method 
used to access the request.
+                </xs:documentation>
+            </xs:annotation>
+            <xs:simpleType>
+                <xs:restriction base="xs:token">
+                    <xs:enumeration value="get"/>
+                    <xs:enumeration value="post"/>
+                    <xs:enumeration value="put"/>
+                    <xs:enumeration value="delete"/>
+                    <xs:enumeration value="patch"/>
+                    <xs:enumeration value="options"/>
+                    <xs:enumeration value="all"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
         <xs:attribute type="xs:boolean" name="edit" default="true">
             <xs:annotation>
                 <xs:documentation>

Modified: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java?rev=1838081&r1=1838080&r2=1838081&view=diff
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java
 (original)
+++ 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java
 Wed Aug 15 11:45:45 2018
@@ -49,6 +49,8 @@ import org.apache.ofbiz.base.util.UtilVa
 import org.apache.ofbiz.base.util.UtilXml;
 import org.apache.ofbiz.base.util.cache.UtilCache;
 import org.apache.ofbiz.base.util.collections.MapContext;
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContextAdapter;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
@@ -192,7 +194,7 @@ public class ConfigXMLReader {
         private Map<String, Event> beforeLogoutEventList = new 
LinkedHashMap<String, Event>();
         private Map<String, String> eventHandlerMap = new HashMap<String, 
String>();
         private Map<String, String> viewHandlerMap = new HashMap<String, 
String>();
-        private Map<String, RequestMap> requestMapMap = new HashMap<String, 
RequestMap>();
+        private MultivaluedMapContext<String, RequestMap> requestMapMap = new 
MultivaluedMapContext<>();
         private Map<String, ViewMap> viewMapMap = new HashMap<String, 
ViewMap>();
 
         public ControllerConfig(URL url) throws WebAppConfigurationException {
@@ -276,11 +278,16 @@ public class ConfigXMLReader {
             return getIncludes(ccfg -> ccfg.protectView);
         }
 
+        // XXX: Keep it for backward compatibility until moving everything to 
鈥榞etRequestMapMultiMap鈥�.
         public Map<String, RequestMap> getRequestMapMap() throws 
WebAppConfigurationException {
-            MapContext<String, RequestMap> result = new MapContext<>();
+            return new MultivaluedMapContextAdapter<>(getRequestMapMultiMap());
+        }
+
+        public MultivaluedMapContext<String, RequestMap> 
getRequestMapMultiMap() throws WebAppConfigurationException {
+            MultivaluedMapContext<String, RequestMap> result = new 
MultivaluedMapContext<>();
             for (URL includeLocation : includes) {
                 ControllerConfig controllerConfig = 
getControllerConfig(includeLocation);
-                result.push(controllerConfig.getRequestMapMap());
+                result.push(controllerConfig.getRequestMapMultiMap());
             }
             result.push(requestMapMap);
             return result;
@@ -403,7 +410,7 @@ public class ConfigXMLReader {
         private void loadRequestMap(Element root) {
             for (Element requestMapElement : UtilXml.childElementList(root, 
"request-map")) {
                 RequestMap requestMap = new RequestMap(requestMapElement);
-                this.requestMapMap.put(requestMap.uri, requestMap);
+                this.requestMapMap.add(requestMap.uri, requestMap);
             }
         }
 
@@ -450,6 +457,7 @@ public class ConfigXMLReader {
 
     public static class RequestMap {
         public String uri;
+        public String method;
         public boolean edit = true;
         public boolean trackVisit = true;
         public boolean trackServerHit = true;
@@ -466,6 +474,7 @@ public class ConfigXMLReader {
         public RequestMap(Element requestMapElement) {
             // Get the URI info
             this.uri = requestMapElement.getAttribute("uri");
+            this.method = requestMapElement.getAttribute("method");
             this.edit = 
!"false".equals(requestMapElement.getAttribute("edit"));
             this.trackServerHit = 
!"false".equals(requestMapElement.getAttribute("track-serverhit"));
             this.trackVisit = 
!"false".equals(requestMapElement.getAttribute("track-visit"));

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=1838081&r1=1838080&r2=1838081&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
 Wed Aug 15 11:45:45 2018
@@ -211,6 +211,12 @@ public class ControlServlet extends Http
         try {
             // the ServerHitBin call for the event is done inside the 
doRequest method
             requestHandler.doRequest(request, response, null, userLogin, 
delegator);
+        } catch (MethodNotAllowedException e) {
+            response.setContentType("text/plain");
+            response.setCharacterEncoding(request.getCharacterEncoding());
+            response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+            response.getWriter().print(e.getMessage());
+            Debug.logError(e.getMessage(), module);
         } catch (RequestHandlerException e) {
             Throwable throwable = e.getNested() != null ? e.getNested() : e;
             if (throwable instanceof IOException) {

Added: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
URL: 
http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java?rev=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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.ofbiz.webapp.control;
+
+@SuppressWarnings("serial")
+public class MethodNotAllowedException extends RequestHandlerException {
+    MethodNotAllowedException(String str) {
+        super(str);
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/MethodNotAllowedException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1838081&r1=1838080&r2=1838081&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
 Wed Aug 15 11:45:45 2018
@@ -24,11 +24,15 @@ import java.io.IOException;
 import java.io.Serializable;
 import java.net.URL;
 import java.security.cert.X509Certificate;
+
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
@@ -45,12 +49,14 @@ import org.apache.ofbiz.base.util.UtilMi
 import org.apache.ofbiz.base.util.UtilObject;
 import org.apache.ofbiz.base.util.UtilProperties;
 import org.apache.ofbiz.base.util.UtilValidate;
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
 import org.apache.ofbiz.entity.Delegator;
 import org.apache.ofbiz.entity.GenericEntityException;
 import org.apache.ofbiz.entity.GenericValue;
 import org.apache.ofbiz.entity.util.EntityQuery;
 import org.apache.ofbiz.entity.util.EntityUtilProperties;
 import org.apache.ofbiz.webapp.OfbizUrlBuilder;
+import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap;
 import org.apache.ofbiz.webapp.event.EventFactory;
 import org.apache.ofbiz.webapp.event.EventHandler;
 import org.apache.ofbiz.webapp.event.EventHandlerException;
@@ -68,12 +74,74 @@ import org.apache.ofbiz.widget.model.The
 public class RequestHandler {
 
     public static final String module = RequestHandler.class.getName();
-    private final String defaultStatusCodeString = 
UtilProperties.getPropertyValue("requestHandler", "status-code", "302");
+    private final static String defaultStatusCodeString =
+            UtilProperties.getPropertyValue("requestHandler", "status-code", 
"302");
     private final ViewFactory viewFactory;
     private final EventFactory eventFactory;
     private final URL controllerConfigURL;
     private final boolean trackServerHit;
     private final boolean trackVisit;
+    private ControllerConfig ccfg;
+
+    static class ControllerConfig {
+        private final MultivaluedMapContext<String, RequestMap> requestMapMap;
+        private final Map<String, ConfigXMLReader.ViewMap> viewMapMap;
+        private String statusCodeString;
+        private final String defaultRequest;
+        private final Map<String, ConfigXMLReader.Event> firstVisitEventList;
+        private final Map<String, ConfigXMLReader.Event> preprocessorEventList;
+        private final Map<String, ConfigXMLReader.Event> 
postprocessorEventList;
+        private final String protectView;
+
+        ControllerConfig(ConfigXMLReader.ControllerConfig ccfg) throws 
WebAppConfigurationException {
+            preprocessorEventList = ccfg.getPreprocessorEventList();
+            postprocessorEventList = ccfg.getPostprocessorEventList();
+            requestMapMap = ccfg.getRequestMapMultiMap();
+            viewMapMap = ccfg.getViewMapMap();
+            defaultRequest = ccfg.getDefaultRequest();
+            firstVisitEventList = ccfg.getFirstVisitEventList();
+            protectView = ccfg.getProtectView();
+
+            String status = ccfg.getStatusCode();
+            statusCodeString = UtilValidate.isEmpty(status) ? 
defaultStatusCodeString : status;
+        }
+
+        public MultivaluedMapContext<String, RequestMap> getRequestMapMap() {
+            return requestMapMap;
+        }
+
+        public Map<String, ConfigXMLReader.ViewMap> getViewMapMap() {
+            return viewMapMap;
+        }
+
+        public String getStatusCodeString() {
+            return statusCodeString;
+        }
+
+        public String getDefaultRequest() {
+            return defaultRequest;
+        }
+
+        public void setStatusCodeString(String statusCodeString) {
+            this.statusCodeString = statusCodeString;
+        }
+
+        public Map<String, ConfigXMLReader.Event> getFirstVisitEventList() {
+            return firstVisitEventList;
+        }
+
+        public Map<String, ConfigXMLReader.Event> getPreprocessorEventList() {
+            return preprocessorEventList;
+        }
+
+        public Map<String, ConfigXMLReader.Event> getPostprocessorEventList() {
+            return postprocessorEventList;
+        }
+
+        public String getProtectView() {
+            return protectView;
+        }
+    }
 
     public static RequestHandler getRequestHandler(ServletContext 
servletContext) {
         RequestHandler rh = (RequestHandler) 
servletContext.getAttribute("_REQUEST_HANDLER_");
@@ -110,6 +178,56 @@ public class RequestHandler {
         return null;
     }
 
+    /**
+     * Find 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
+     * @param req The HTTP request to match
+     * @return a collection of request maps which might be empty
+     */
+    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) && 
!viewMapMap.containsKey(viewUri)) {
+            rmaps = requestMapMap.get(requestUri);
+        } else if (defaultRequest != null) {
+            rmaps = requestMapMap.get(defaultRequest);
+        } else {
+            rmaps = null;
+        }
+        return rmaps != null ? rmaps : Collections.emptyList();
+    }
+
+    /**
+     * Find the request map matching {@code method}.
+     * Otherwise fall back to the one matching the "all" and "" special methods
+     * in that respective order.
+     *
+     * @param method the HTTP method to match
+     * @param rmaps the collection of request map candidates
+     * @return a request map {@code Optional}
+     */
+    static Optional<RequestMap> resolveMethod(String method, 
Collection<RequestMap> rmaps) {
+        for (RequestMap map : rmaps) {
+            if (map.method.equalsIgnoreCase(method)) {
+                return Optional.of(map);
+            }
+        }
+        if (method.isEmpty()) {
+            return Optional.empty();
+        } else if (method.equals("all")) {
+            return resolveMethod("", rmaps);
+        } else {
+            return resolveMethod("all", rmaps);
+        }
+    }
+
     public void doRequest(HttpServletRequest request, HttpServletResponse 
response, String requestUri) throws RequestHandlerException, 
RequestHandlerExceptionAllowExternalRequests {
         HttpSession session = request.getSession();
         Delegator delegator = (Delegator) request.getAttribute("delegator");
@@ -125,20 +243,13 @@ public class RequestHandler {
         long startTime = System.currentTimeMillis();
         HttpSession session = request.getSession();
 
-        // get the controllerConfig once for this method so we don't have to 
get it over and over inside the method
-        ConfigXMLReader.ControllerConfig controllerConfig = 
this.getControllerConfig();
-        Map<String, ConfigXMLReader.RequestMap> requestMapMap = null;
-        String statusCodeString = null;
+        // Parse controller config.
         try {
-            requestMapMap = controllerConfig.getRequestMapMap();
-            statusCodeString = controllerConfig.getStatusCode();
+            ccfg = new ControllerConfig(getControllerConfig());
         } catch (WebAppConfigurationException e) {
             Debug.logError(e, "Exception thrown while parsing controller.xml 
file: ", module);
             throw new RequestHandlerException(e);
         }
-        if (UtilValidate.isEmpty(statusCodeString)) {
-            statusCodeString = defaultStatusCodeString;
-        }
 
         // workaround if we are in the root webapp
         String cname = UtilHttp.getApplicationName(request);
@@ -153,50 +264,29 @@ public class RequestHandler {
             }
         }
 
-        String overrideViewUri = 
RequestHandler.getOverrideViewUri(request.getPathInfo());
-
-        String requestMissingErrorMessage = "Unknown request [" + 
defaultRequestUri + "]; this request does not exist or cannot be called 
directly.";
-        ConfigXMLReader.RequestMap requestMap = null;
-        if (defaultRequestUri != null) {
-            requestMap = requestMapMap.get(defaultRequestUri);
-        }
-        // check for default request
-        if (requestMap == null) {
-            String defaultRequest;
-            try {
-                defaultRequest = controllerConfig.getDefaultRequest();
-            } catch (WebAppConfigurationException e) {
-                Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                throw new RequestHandlerException(e);
-            }
-            if (defaultRequest != null) { // required! to avoid a null pointer 
exception and generate a requesthandler exception if default request not found.
-                requestMap = requestMapMap.get(defaultRequest);
-            }
-        }
-
-        // check for override view
-        if (overrideViewUri != null) {
-            ConfigXMLReader.ViewMap viewMap;
-            try {
-                viewMap = 
getControllerConfig().getViewMapMap().get(overrideViewUri);
-                if (viewMap == null) {
-                    String defaultRequest = 
controllerConfig.getDefaultRequest();
-                    if (defaultRequest != null) { // required! to avoid a null 
pointer exception and generate a requesthandler exception if default request 
not found.
-                        requestMap = requestMapMap.get(defaultRequest);
-                    }
-                }
-            } catch (WebAppConfigurationException e) {
-                Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                throw new RequestHandlerException(e);
+        String requestMissingErrorMessage = "Unknown request ["
+                + defaultRequestUri
+                + "]; this request does not exist or cannot be called 
directly.";
+
+        String path = request.getPathInfo();
+        String requestUri = getRequestUri(path);
+        String overrideViewUri = getOverrideViewUri(path);
+
+        Collection<RequestMap> rmaps = resolveURI(ccfg, request);
+        if (rmaps.isEmpty()) {
+            if (throwRequestHandlerExceptionOnMissingLocalRequest) {
+              throw new RequestHandlerException(requestMissingErrorMessage);
+            } else {
+              throw new RequestHandlerExceptionAllowExternalRequests();
             }
         }
 
-        // if no matching request is found in the controller, depending on 
throwRequestHandlerExceptionOnMissingLocalRequest
-        //  we throw a RequestHandlerException or 
RequestHandlerExceptionAllowExternalRequests
-        if (requestMap == null) {
-            if (throwRequestHandlerExceptionOnMissingLocalRequest) throw new 
RequestHandlerException(requestMissingErrorMessage);
-            else throw new RequestHandlerExceptionAllowExternalRequests();
-         }
+        String method = request.getMethod();
+        RequestMap requestMap = resolveMethod(method, rmaps).orElseThrow(() -> 
{
+            String msg = UtilProperties.getMessage("WebappUiLabels", 
"RequestMethodNotMatchConfig",
+                    UtilMisc.toList(requestUri, method), 
UtilHttp.getLocale(request));
+            return new MethodNotAllowedException(msg);
+        });
 
         String eventReturn = null;
         if (requestMap.metrics != null && requestMap.metrics.getThreshold() != 
0.0 && requestMap.metrics.getTotalEvents() > 3 && 
requestMap.metrics.getThreshold() < requestMap.metrics.getServiceRate()) {
@@ -210,7 +300,7 @@ public class RequestHandler {
         // Check for chained request.
         if (chain != null) {
             String chainRequestUri = RequestHandler.getRequestUri(chain);
-            requestMap = requestMapMap.get(chainRequestUri);
+            requestMap = ccfg.getRequestMapMap().getFirst(chainRequestUri);
             if (requestMap == null) {
                 throw new RequestHandlerException("Unknown chained request [" 
+ chainRequestUri + "]; this request does not exist");
             }
@@ -234,18 +324,11 @@ public class RequestHandler {
             // Check to make sure we are allowed to access this request 
directly. (Also checks if this request is defined.)
             // If the request cannot be called, or is not defined, check and 
see if there is a default-request we can process
             if (!requestMap.securityDirectRequest) {
-                String defaultRequest;
-                try {
-                    defaultRequest = controllerConfig.getDefaultRequest();
-                } catch (WebAppConfigurationException e) {
-                    Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                    throw new RequestHandlerException(e);
-                }
-                if (defaultRequest == null || 
!requestMapMap.get(defaultRequest).securityDirectRequest) {
+                if (ccfg.getDefaultRequest() == null || 
!ccfg.getRequestMapMap().getFirst(ccfg.getDefaultRequest()).securityDirectRequest)
 {
                     // use the same message as if it was missing for security 
reasons, ie so can't tell if it is missing or direct request is not allowed
                     throw new 
RequestHandlerException(requestMissingErrorMessage);
                 } else {
-                    requestMap = requestMapMap.get(defaultRequest);
+                    requestMap = 
ccfg.getRequestMapMap().getFirst(ccfg.getDefaultRequest());
                 }
             }
             // Check if we SHOULD be secure and are not.
@@ -288,7 +371,7 @@ public class RequestHandler {
                     String newUrl = RequestHandler.makeUrl(request, response, 
urlBuf.toString());
                     if (newUrl.toUpperCase().startsWith("HTTPS")) {
                         // if we are supposed to be secure, redirect secure.
-                        callRedirect(newUrl, response, request, 
statusCodeString);
+                        callRedirect(newUrl, response, request, 
ccfg.getStatusCodeString());
                         return;
                     }
                 }
@@ -333,63 +416,52 @@ public class RequestHandler {
                 if (Debug.infoOn())
                     Debug.logInfo("This is the first request in this visit." + 
showSessionId(request), module);
                 session.setAttribute("_FIRST_VISIT_EVENTS_", "complete");
-                try {
-                    for (ConfigXMLReader.Event event: 
controllerConfig.getFirstVisitEventList().values()) {
-                        try {
-                            String returnString = this.runEvent(request, 
response, event, null, "firstvisit");
-                            if (returnString == null || 
"none".equalsIgnoreCase(returnString)) {
-                                interruptRequest = true;
-                            } else if 
(!"success".equalsIgnoreCase(returnString)) {
-                                throw new EventHandlerException("First-Visit 
event did not return 'success'.");
-                            }
-                        } catch (EventHandlerException e) {
-                            Debug.logError(e, module);
+                for (ConfigXMLReader.Event event: 
ccfg.getFirstVisitEventList().values()) {
+                    try {
+                        String returnString = this.runEvent(request, response, 
event, null, "firstvisit");
+                        if (returnString == null || 
"none".equalsIgnoreCase(returnString)) {
+                            interruptRequest = true;
+                        } else if (!"success".equalsIgnoreCase(returnString)) {
+                            throw new EventHandlerException("First-Visit event 
did not return 'success'.");
                         }
+                    } catch (EventHandlerException e) {
+                        Debug.logError(e, module);
                     }
-                } catch (WebAppConfigurationException e) {
-                    Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                    throw new RequestHandlerException(e);
                 }
             }
 
             // Invoke the pre-processor (but NOT in a chain)
-            try {
-                for (ConfigXMLReader.Event event: 
controllerConfig.getPreprocessorEventList().values()) {
-                    try {
-                        String returnString = this.runEvent(request, response, 
event, null, "preprocessor");
-                        if (returnString == null || 
"none".equalsIgnoreCase(returnString)) {
-                            interruptRequest = true;
-                        } else if (!"success".equalsIgnoreCase(returnString)) {
-                            if (!returnString.contains(":_protect_:")) {
-                                throw new EventHandlerException("Pre-Processor 
event [" + event.invoke + "] did not return 'success'.");
-                            } else { // protect the view normally rendered and 
redirect to error response view
-                                returnString = 
returnString.replace(":_protect_:", "");
-                                if (returnString.length() > 0) {
-                                    request.setAttribute("_ERROR_MESSAGE_", 
returnString);
-                                }
-                                eventReturn = null;
-                                // check to see if there is a "protect" 
response, if so it's ok else show the default_error_response_view
-                                if 
(!requestMap.requestResponseMap.containsKey("protect")) {
-                                    String protectView = 
controllerConfig.getProtectView();
-                                    if (protectView != null) {
-                                        overrideViewUri = protectView;
-                                    } else {
-                                        overrideViewUri = 
EntityUtilProperties.getPropertyValue("security", 
"default.error.response.view", delegator);
-                                        overrideViewUri = 
overrideViewUri.replace("view:", "");
-                                        if ("none:".equals(overrideViewUri)) {
-                                            interruptRequest = true;
-                                        }
+            for (ConfigXMLReader.Event event: 
ccfg.getPreprocessorEventList().values()) {
+                try {
+                    String returnString = this.runEvent(request, response, 
event, null, "preprocessor");
+                    if (returnString == null || 
"none".equalsIgnoreCase(returnString)) {
+                        interruptRequest = true;
+                    } else if (!"success".equalsIgnoreCase(returnString)) {
+                        if (!returnString.contains(":_protect_:")) {
+                            throw new EventHandlerException("Pre-Processor 
event [" + event.invoke + "] did not return 'success'.");
+                        } else { // protect the view normally rendered and 
redirect to error response view
+                            returnString = returnString.replace(":_protect_:", 
"");
+                            if (returnString.length() > 0) {
+                                request.setAttribute("_ERROR_MESSAGE_", 
returnString);
+                            }
+                            eventReturn = null;
+                            // check to see if there is a "protect" response, 
if so it's ok else show the default_error_response_view
+                            if 
(!requestMap.requestResponseMap.containsKey("protect")) {
+                                if (ccfg.getProtectView() != null) {
+                                    overrideViewUri = ccfg.getProtectView();
+                                } else {
+                                    overrideViewUri = 
EntityUtilProperties.getPropertyValue("security", 
"default.error.response.view", delegator);
+                                    overrideViewUri = 
overrideViewUri.replace("view:", "");
+                                    if ("none:".equals(overrideViewUri)) {
+                                        interruptRequest = true;
                                     }
                                 }
                             }
                         }
-                    } catch (EventHandlerException e) {
-                        Debug.logError(e, module);
                     }
+                } catch (EventHandlerException e) {
+                    Debug.logError(e, module);
                 }
-            } catch (WebAppConfigurationException e) {
-                Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                throw new RequestHandlerException(e);
             }
         }
 
@@ -409,7 +481,7 @@ public class RequestHandler {
             // Invoke the security handler
             // catch exceptions and throw RequestHandlerException if failed.
             if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler]: 
AuthRequired. Running security check. " + showSessionId(request), module);
-            ConfigXMLReader.Event checkLoginEvent = 
requestMapMap.get("checkLogin").event;
+            ConfigXMLReader.Event checkLoginEvent = 
ccfg.getRequestMapMap().getFirst("checkLogin").event;
             String checkLoginReturnString = null;
 
             try {
@@ -422,9 +494,9 @@ public class RequestHandler {
                 eventReturn = checkLoginReturnString;
                 // if the request is an ajax request we don't want to return 
the default login check
                 if 
(!"XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
-                    requestMap = requestMapMap.get("checkLogin");
+                    requestMap = 
ccfg.getRequestMapMap().getFirst("checkLogin");
                 } else {
-                    requestMap = requestMapMap.get("ajaxCheckLogin");
+                    requestMap = 
ccfg.getRequestMapMap().getFirst("ajaxCheckLogin");
                 }
             }
         }
@@ -556,7 +628,7 @@ public class RequestHandler {
                     redirectTarget += "?" + queryString;
                 }
                 
-                callRedirect(makeLink(request, response, redirectTarget), 
response, request, statusCodeString);
+                callRedirect(makeLink(request, response, redirectTarget), 
response, request, ccfg.getStatusCodeString());
                 return;
             }
         }
@@ -603,41 +675,35 @@ public class RequestHandler {
             // ======== handle views ========
 
             // first invoke the post-processor events.
-            try {
-                for (ConfigXMLReader.Event event: 
controllerConfig.getPostprocessorEventList().values()) {
-                    try {
-                        String returnString = this.runEvent(request, response, 
event, requestMap, "postprocessor");
-                        if (returnString != null && 
!"success".equalsIgnoreCase(returnString)) {
-                            throw new EventHandlerException("Post-Processor 
event did not return 'success'.");
-                        }
-                    } catch (EventHandlerException e) {
-                        Debug.logError(e, module);
+            for (ConfigXMLReader.Event event: 
ccfg.getPostprocessorEventList().values()) {
+                try {
+                    String returnString = this.runEvent(request, response, 
event, requestMap, "postprocessor");
+                    if (returnString != null && 
!"success".equalsIgnoreCase(returnString)) {
+                        throw new EventHandlerException("Post-Processor event 
did not return 'success'.");
                     }
+                } catch (EventHandlerException e) {
+                    Debug.logError(e, module);
                 }
-            } catch (WebAppConfigurationException e) {
-                Debug.logError(e, "Exception thrown while parsing 
controller.xml file: ", module);
-                throw new RequestHandlerException(e);
             }
 
             String responseStatusCode  = nextRequestResponse.statusCode;
             if(UtilValidate.isNotEmpty(responseStatusCode))
-                statusCodeString = responseStatusCode;            
-            
+                ccfg.setStatusCodeString(responseStatusCode);
             
             if ("url".equals(nextRequestResponse.type)) {
                 if (Debug.verboseOn()) 
Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect." + 
showSessionId(request), module);
-                callRedirect(nextRequestResponse.value, response, request, 
statusCodeString);
+                callRedirect(nextRequestResponse.value, response, request, 
ccfg.getStatusCodeString());
             } else if ("cross-redirect".equals(nextRequestResponse.type)) {
                 // check for a cross-application redirect
                 if (Debug.verboseOn()) 
Debug.logVerbose("[RequestHandler.doRequest]: Response is a Cross-Application 
redirect." + showSessionId(request), module);
                 String url = nextRequestResponse.value.startsWith("/") ? 
nextRequestResponse.value : "/" + nextRequestResponse.value;
-                callRedirect(url + this.makeQueryString(request, 
nextRequestResponse), response, request, statusCodeString);
+                callRedirect(url + this.makeQueryString(request, 
nextRequestResponse), response, request, ccfg.getStatusCodeString());
             } else if ("request-redirect".equals(nextRequestResponse.type)) {
                 if (Debug.verboseOn()) 
Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect." 
+ showSessionId(request), module);
-                callRedirect(makeLinkWithQueryString(request, response, "/" + 
nextRequestResponse.value, nextRequestResponse), response, request, 
statusCodeString);
+                callRedirect(makeLinkWithQueryString(request, response, "/" + 
nextRequestResponse.value, nextRequestResponse), response, request, 
ccfg.getStatusCodeString());
             } else if 
("request-redirect-noparam".equals(nextRequestResponse.type)) {
                 if (Debug.verboseOn()) 
Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect 
with no parameters." + showSessionId(request), module);
-                callRedirect(makeLink(request, response, 
nextRequestResponse.value), response, request, statusCodeString);
+                callRedirect(makeLink(request, response, 
nextRequestResponse.value), response, request, ccfg.getStatusCodeString());
             } else if ("view".equals(nextRequestResponse.type)) {
                 if (Debug.verboseOn()) 
Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + 
showSessionId(request), module);
 

Added: 
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=1838081&view=auto
==============================================================================
--- 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
 (added)
+++ 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
 Wed Aug 15 11:45:45 2018
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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.ofbiz.webapp.control;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ofbiz.base.util.collections.MultivaluedMapContext;
+import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap;
+import org.apache.ofbiz.webapp.control.ConfigXMLReader.ViewMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Element;
+
+public class RequestHandlerTests {
+    public static class ResolveURITests {
+        private MultivaluedMapContext<String,RequestMap> reqMaps;
+        private Map<String, ViewMap> viewMaps;
+        private HttpServletRequest req;
+        private Element dummyElement;
+        private RequestHandler.ControllerConfig ccfg;
+
+        @Before
+        public void setUp() {
+            ccfg = mock(RequestHandler.ControllerConfig.class);
+            reqMaps = new MultivaluedMapContext<>();
+            viewMaps = new HashMap<>();
+            when(ccfg.getDefaultRequest()).thenReturn(null);
+            when(ccfg.getRequestMapMap()).thenReturn(reqMaps);
+            when(ccfg.getViewMapMap()).thenReturn(viewMaps);
+            req = mock(HttpServletRequest.class);
+            dummyElement = mock(Element.class);
+            when(dummyElement.getAttribute("method")).thenReturn("all");
+            when(req.getMethod()).thenReturn("get");
+        }
+
+        @Test
+        public void resolveURIBasic() throws RequestHandlerException {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo", foo);
+            reqMaps.putSingle("bar", bar);
+            when(req.getPathInfo()).thenReturn("/foo");
+            assertThat(RequestHandler.resolveURI(ccfg, req),
+                    both(hasItem(foo)).and(not(hasItem(bar))));
+            assertThat(RequestHandler.resolveURI(ccfg, req).size(), is(1));
+        }
+
+        @Test
+        public void resolveURIBasicPut() throws RequestHandlerException {
+            when(dummyElement.getAttribute("method")).thenReturn("put");
+            when(req.getPathInfo()).thenReturn("/foo");
+            when(req.getMethod()).thenReturn("put");
+
+            RequestMap foo = new RequestMap(dummyElement);
+
+            assertTrue(RequestHandler.resolveURI(ccfg, req).isEmpty());
+            reqMaps.putSingle("foo", foo);
+            assertFalse(RequestHandler.resolveURI(ccfg, req).isEmpty());
+        }
+
+        @Test
+        public void resolveURIUpperCase() throws RequestHandlerException {
+            when(dummyElement.getAttribute("method")).thenReturn("get");
+            RequestMap foo = new RequestMap(dummyElement);
+            when(dummyElement.getAttribute("method")).thenReturn("put");
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo", foo);
+            reqMaps.putSingle("bar", bar);
+
+            when(req.getPathInfo()).thenReturn("/foo");
+            when(req.getMethod()).thenReturn("GET");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(foo));
+
+            when(req.getPathInfo()).thenReturn("/bar");
+            when(req.getMethod()).thenReturn("PUT");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(bar));
+        }
+
+        @Test
+        public void resolveURIDefault() throws Exception {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo", foo);
+            reqMaps.putSingle("bar", bar);
+
+            when(req.getPathInfo()).thenReturn("/baz");
+            when(ccfg.getDefaultRequest()).thenReturn("bar");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(bar));
+        }
+
+        @Test
+        public void resolveURIOverrideView() throws Exception {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo", foo);
+            reqMaps.putSingle("bar", bar);
+
+            viewMaps.put("baz", new ViewMap(dummyElement));
+
+            when(req.getPathInfo()).thenReturn("/foo/baz");
+            when(ccfg.getDefaultRequest()).thenReturn("bar");
+            assertThat(RequestHandler.resolveURI(ccfg, req), hasItem(bar));
+        }
+
+        @Test
+        public void resolveURINoDefault() throws Exception {
+            RequestMap foo = new RequestMap(dummyElement);
+            RequestMap bar = new RequestMap(dummyElement);
+            reqMaps.putSingle("foo", foo);
+            reqMaps.putSingle("bar", bar);
+
+            when(req.getPathInfo()).thenReturn("/baz");
+            assertTrue(RequestHandler.resolveURI(ccfg, req).isEmpty());
+        }
+    }
+
+    public static class ResolveMethodTests {
+        private Element dummyElement;
+        private Collection<RequestMap> rmaps;
+
+        @Before
+        public void setUp() {
+            dummyElement = mock(Element.class);
+            rmaps = new ArrayList<>();
+        }
+
+        @Test
+        public void resolveMethodBasic() throws RequestHandlerException {
+            RequestMap fooPut = new RequestMap(dummyElement);
+            fooPut.method = "put";
+            rmaps.add(fooPut);
+
+            RequestMap fooAll = new RequestMap(dummyElement);
+            fooAll.method = "all";
+            rmaps.add(fooAll);
+
+            assertThat(RequestHandler.resolveMethod("put", rmaps).get(), 
is(fooPut));
+            assertThat(RequestHandler.resolveMethod("get", rmaps).get(), 
is(fooAll));
+        }
+
+        @Test
+        public void resolveMethodCatchAll() throws RequestHandlerException {
+            assertFalse(RequestHandler.resolveMethod("get", 
rmaps).isPresent());
+            assertFalse(RequestHandler.resolveMethod("post", 
rmaps).isPresent());
+            assertFalse(RequestHandler.resolveMethod("put", 
rmaps).isPresent());
+            assertFalse(RequestHandler.resolveMethod("delete", 
rmaps).isPresent());
+
+            RequestMap foo = new RequestMap(dummyElement);
+            foo.method = "all";
+            rmaps.add(foo);
+            assertTrue(RequestHandler.resolveMethod("get", rmaps).isPresent());
+            assertTrue(RequestHandler.resolveMethod("post", 
rmaps).isPresent());
+            assertTrue(RequestHandler.resolveMethod("put", rmaps).isPresent());
+            assertTrue(RequestHandler.resolveMethod("delete", 
rmaps).isPresent());
+        }
+    }
+}

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: 
ofbiz/ofbiz-framework/trunk/framework/webapp/src/test/java/org/apache/ofbiz/webapp/control/RequestHandlerTests.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain


Reply via email to