You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ofbiz.apache.org by Shi Jinghai <hu...@hotmail.com> on 2018/08/19 20:07:16 UTC

Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework: base/src/main/java/org/apache/ofbiz/base/util/collections/ base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/ webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...

Hi Deepak,

As Rishi suggested, could you please reopen OFBIZ-10438 or create a new jira for the bug you mentioned here?

I cannot reproduce it in my local and online trunk demo environments, I guess my test case is not the same as yours.

Thanks,

Shi Jinghai

-----邮件原件-----
发件人: Rishi Solanki [mailto:rishisolankii@gmail.com] 
发送时间: 2018年8月17日 21:24
收件人: dev@ofbiz.apache.org
抄送: commits@ofbiz.apache.org
主题: Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework: base/src/main/java/org/apache/ofbiz/base/util/collections/ base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/ webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...

Deepak,

Thanks for reporting the issue, can you please create Jira ticket and add
details (images are not coming at mailing list) with logs if possible?



Rishi Solanki
Sr Manager, Enterprise Software Development
HotWax Systems Pvt. Ltd.
Direct: +91-9893287847
http://www.hotwaxsystems.com
www.hotwax.co

On Fri, Aug 17, 2018 at 6:32 PM, deepak nigam <de...@gmail.com>
wrote:

> I am using OFBiz trunk and on creating an order from backend and getting
> error screens while adding an item to cart and selecting shipping methods.
> Please find the attached images for your reference.
>
> On further investigation, I found that on reverting this commit things are
> working fine. Can we verify this commit once more?
>
> On Wed, Aug 15, 2018 at 5:15 PM <sh...@apache.org> wrote:
>
>> 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 RequestHandlerExceptionAllowEx
>> ternalRequests();
>> -         }
>> +        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");
>> +
>
>

Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework: base/src/main/java/org/apache/ofbiz/base/util/collections/ base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/ webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...

Posted by deepak nigam <de...@gmail.com>.
Thanks, Jacques and Mathieu.

Verified the fix on the local machine, the system is working fine now.


Thanks & Regards
--
Deepak Nigam
HotWax Systems Pvt. Ltd.

On Tue, Aug 28, 2018 at 9:51 PM Jacques Le Roux <
jacques.le.roux@les7arts.com> wrote:

> Hi,
>
> I confirm this issue which can easily be reproduced in trunk and locally.
>
> The problem is r1838081 prevents to serialise requests.
>
> Like in ordermgr/control/updateCheckoutOptions/quickcheckout where
> updateCheckoutOptions and quickcheckout are serialised to update the check
> out with
> updateCheckoutOptions and then get back to the screen with quickcheckout
>
> It's fixed at r1839451 using last Mathieu's patch in OFBIZ-10438
>
> Jacques
>
>
> Le 20/08/2018 à 06:25, deepak nigam a écrit :
> > Hi Shi/Rishi,
> >
> > I was using the trunk downloaded from GitHub (
> > https://github.com/apache/ofbiz-framework). I will reverify this issue
> on
> > SVN trunk and will let you know the details.
> >
> > On Mon, Aug 20, 2018 at 1:37 AM Shi Jinghai <hu...@hotmail.com>
> wrote:
> >
> >> Hi Deepak,
> >>
> >> As Rishi suggested, could you please reopen OFBIZ-10438 or create a new
> >> jira for the bug you mentioned here?
> >>
> >> I cannot reproduce it in my local and online trunk demo environments, I
> >> guess my test case is not the same as yours.
> >>
> >> Thanks,
> >>
> >> Shi Jinghai
> >>
> >> -----邮件原件-----
> >> 发件人: Rishi Solanki [mailto:rishisolankii@gmail.com]
> >> 发送时间: 2018年8月17日 21:24
> >> 收件人: dev@ofbiz.apache.org
> >> 抄送: commits@ofbiz.apache.org
> >> 主题: Re: svn commit: r1838081 - in
> /ofbiz/ofbiz-framework/trunk/framework:
> >> base/src/main/java/org/apache/ofbiz/base/util/collections/
> >> base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/
> >> webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...
> >>
> >> Deepak,
> >>
> >> Thanks for reporting the issue, can you please create Jira ticket and
> add
> >> details (images are not coming at mailing list) with logs if possible?
> >>
> >>
> >>
> >> Rishi Solanki
> >> Sr Manager, Enterprise Software Development
> >> HotWax Systems Pvt. Ltd.
> >> Direct: +91-9893287847
> >> http://www.hotwaxsystems.com
> >> www.hotwax.co
> >>
> >> On Fri, Aug 17, 2018 at 6:32 PM, deepak nigam <
> deepak.nigam1990@gmail.com>
> >> wrote:
> >>
> >>> I am using OFBiz trunk and on creating an order from backend and
> getting
> >>> error screens while adding an item to cart and selecting shipping
> >> methods.
> >>> Please find the attached images for your reference.
> >>>
> >>> On further investigation, I found that on reverting this commit things
> >> are
> >>> working fine. Can we verify this commit once more?
> >>>
> >>> On Wed, Aug 15, 2018 at 5:15 PM <sh...@apache.org> wrote:
> >>>
> >>>> 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 RequestHandlerExceptionAllowEx
> >>>> ternalRequests();
> >>>> -         }
> >>>> +        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 redirec

Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework: base/src/main/java/org/apache/ofbiz/base/util/collections/ base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/ webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...

Posted by Jacques Le Roux <ja...@les7arts.com>.
Hi,

I confirm this issue which can easily be reproduced in trunk and locally.

The problem is r1838081 prevents to serialise requests.

Like in ordermgr/control/updateCheckoutOptions/quickcheckout where updateCheckoutOptions and quickcheckout are serialised to update the check out with 
updateCheckoutOptions and then get back to the screen with quickcheckout

It's fixed at r1839451 using last Mathieu's patch in OFBIZ-10438

Jacques


Le 20/08/2018 à 06:25, deepak nigam a écrit :
> Hi Shi/Rishi,
>
> I was using the trunk downloaded from GitHub (
> https://github.com/apache/ofbiz-framework). I will reverify this issue on
> SVN trunk and will let you know the details.
>
> On Mon, Aug 20, 2018 at 1:37 AM Shi Jinghai <hu...@hotmail.com> wrote:
>
>> Hi Deepak,
>>
>> As Rishi suggested, could you please reopen OFBIZ-10438 or create a new
>> jira for the bug you mentioned here?
>>
>> I cannot reproduce it in my local and online trunk demo environments, I
>> guess my test case is not the same as yours.
>>
>> Thanks,
>>
>> Shi Jinghai
>>
>> -----邮件原件-----
>> 发件人: Rishi Solanki [mailto:rishisolankii@gmail.com]
>> 发送时间: 2018年8月17日 21:24
>> 收件人: dev@ofbiz.apache.org
>> 抄送: commits@ofbiz.apache.org
>> 主题: Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework:
>> base/src/main/java/org/apache/ofbiz/base/util/collections/
>> base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/
>> webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...
>>
>> Deepak,
>>
>> Thanks for reporting the issue, can you please create Jira ticket and add
>> details (images are not coming at mailing list) with logs if possible?
>>
>>
>>
>> Rishi Solanki
>> Sr Manager, Enterprise Software Development
>> HotWax Systems Pvt. Ltd.
>> Direct: +91-9893287847
>> http://www.hotwaxsystems.com
>> www.hotwax.co
>>
>> On Fri, Aug 17, 2018 at 6:32 PM, deepak nigam <de...@gmail.com>
>> wrote:
>>
>>> I am using OFBiz trunk and on creating an order from backend and getting
>>> error screens while adding an item to cart and selecting shipping
>> methods.
>>> Please find the attached images for your reference.
>>>
>>> On further investigation, I found that on reverting this commit things
>> are
>>> working fine. Can we verify this commit once more?
>>>
>>> On Wed, Aug 15, 2018 at 5:15 PM <sh...@apache.org> wrote:
>>>
>>>> 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 RequestHandlerExceptionAllowEx
>>>> ternalRequests();
>>>> -         }
>>>> +        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());
>>>>


Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework: base/src/main/java/org/apache/ofbiz/base/util/collections/ base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/ webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...

Posted by deepak nigam <de...@gmail.com>.
Hi Shi/Rishi,

I was using the trunk downloaded from GitHub (
https://github.com/apache/ofbiz-framework). I will reverify this issue on
SVN trunk and will let you know the details.

On Mon, Aug 20, 2018 at 1:37 AM Shi Jinghai <hu...@hotmail.com> wrote:

> Hi Deepak,
>
> As Rishi suggested, could you please reopen OFBIZ-10438 or create a new
> jira for the bug you mentioned here?
>
> I cannot reproduce it in my local and online trunk demo environments, I
> guess my test case is not the same as yours.
>
> Thanks,
>
> Shi Jinghai
>
> -----邮件原件-----
> 发件人: Rishi Solanki [mailto:rishisolankii@gmail.com]
> 发送时间: 2018年8月17日 21:24
> 收件人: dev@ofbiz.apache.org
> 抄送: commits@ofbiz.apache.org
> 主题: Re: svn commit: r1838081 - in /ofbiz/ofbiz-framework/trunk/framework:
> base/src/main/java/org/apache/ofbiz/base/util/collections/
> base/src/test/java/org/apache/ofbiz/base/collections/ webapp/config/
> webapp/dtd/ webapp/src/main/java/org/apache/ofbiz/weba...
>
> Deepak,
>
> Thanks for reporting the issue, can you please create Jira ticket and add
> details (images are not coming at mailing list) with logs if possible?
>
>
>
> Rishi Solanki
> Sr Manager, Enterprise Software Development
> HotWax Systems Pvt. Ltd.
> Direct: +91-9893287847
> http://www.hotwaxsystems.com
> www.hotwax.co
>
> On Fri, Aug 17, 2018 at 6:32 PM, deepak nigam <de...@gmail.com>
> wrote:
>
> > I am using OFBiz trunk and on creating an order from backend and getting
> > error screens while adding an item to cart and selecting shipping
> methods.
> > Please find the attached images for your reference.
> >
> > On further investigation, I found that on reverting this commit things
> are
> > working fine. Can we verify this commit once more?
> >
> > On Wed, Aug 15, 2018 at 5:15 PM <sh...@apache.org> wrote:
> >
> >> 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 RequestHandlerExceptionAllowEx
> >> ternalRequests();
> >> -         }
> >> +        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());
> >>