You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by gg...@apache.org on 2017/04/18 05:45:40 UTC

svn commit: r1791744 - in /httpcomponents/httpcore/trunk/httpcore5/src: main/java/org/apache/hc/core5/http/protocol/ test/java/org/apache/hc/core5/http/protocol/

Author: ggregory
Date: Tue Apr 18 05:45:40 2017
New Revision: 1791744

URL: http://svn.apache.org/viewvc?rev=1791744&view=rev
Log:
[HTTPCORE-452] Add a UriRegexMatcher. Add the new class. Make the current UriPatternMatcher implement the new interface LookupRegistry (whose methods where pulled up from UriPatternMatcher.)

Added:
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java
Modified:
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java

Added: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java?rev=1791744&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/LookupRegistry.java Tue Apr 18 05:45:40 2017
@@ -0,0 +1,59 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.protocol;
+
+/**
+ * A lookup registry.
+ *
+ * @param <T> The type of objects to register and lookup.
+ */
+public interface LookupRegistry<T> {
+
+    /**
+     * Registers the given object for URIs matching the given pattern.
+     *
+     * @param pattern the pattern to register the handler for.
+     * @param obj the object.
+     */
+    void register(String pattern, T obj);
+
+    /**
+     * Looks up an object matching the given request path.
+     *
+     * @param path the request path
+     * @return object or {@code null} if no match is found.
+     */
+    T lookup(String string);
+
+    /**
+     * Removes registered object, if exists, for the given pattern.
+     *
+     * @param pattern the pattern to unregister.
+     */
+    void unregister(String pattern);
+
+}

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java?rev=1791744&r1=1791743&r2=1791744&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriPatternMatcher.java Tue Apr 18 05:45:40 2017
@@ -35,22 +35,20 @@ import org.apache.hc.core5.annotation.Th
 import org.apache.hc.core5.util.Args;
 
 /**
- * Maintains a map of objects keyed by a request URI pattern.
- * <br>
+ * Maintains a map of objects keyed by a request URI pattern. <br>
  * Patterns may have three formats:
  * <ul>
- *   <li>{@code *}</li>
- *   <li>{@code *&lt;uri&gt;}</li>
- *   <li>{@code &lt;uri&gt;*}</li>
+ * <li>{@code *}</li>
+ * <li>{@code *&lt;uri&gt;}</li>
+ * <li>{@code &lt;uri&gt;*}</li>
  * </ul>
  * <br>
- * This class can be used to resolve an object matching a particular request
- * URI.
+ * This class can be used to resolve an object matching a particular request URI.
  *
  * @since 4.0
  */
 @Contract(threading = ThreadingBehavior.SAFE)
-public class UriPatternMatcher<T> {
+public class UriPatternMatcher<T> implements LookupRegistry<T> {
 
     private final Map<String, T> map;
 
@@ -62,9 +60,12 @@ public class UriPatternMatcher<T> {
     /**
      * Registers the given object for URIs matching the given pattern.
      *
-     * @param pattern the pattern to register the handler for.
-     * @param obj the object.
+     * @param pattern
+     *            the pattern to register the handler for.
+     * @param obj
+     *            the object.
      */
+    @Override
     public synchronized void register(final String pattern, final T obj) {
         Args.notNull(pattern, "URI request pattern");
         this.map.put(pattern, obj);
@@ -73,8 +74,10 @@ public class UriPatternMatcher<T> {
     /**
      * Removes registered object, if exists, for the given pattern.
      *
-     * @param pattern the pattern to unregister.
+     * @param pattern
+     *            the pattern to unregister.
      */
+    @Override
     public synchronized void unregister(final String pattern) {
         if (pattern == null) {
             return;
@@ -85,9 +88,11 @@ public class UriPatternMatcher<T> {
     /**
      * Looks up an object matching the given request path.
      *
-     * @param path the request path
+     * @param path
+     *            the request path
      * @return object or {@code null} if no match is found.
      */
+    @Override
     public synchronized T lookup(final String path) {
         Args.notNull(path, "Request path");
         // direct match?
@@ -98,8 +103,7 @@ public class UriPatternMatcher<T> {
             for (final String pattern : this.map.keySet()) {
                 if (matchUriRequestPattern(pattern, path)) {
                     // we have a match. is it any better?
-                    if (bestMatch == null
-                            || (bestMatch.length() < pattern.length())
+                    if (bestMatch == null || (bestMatch.length() < pattern.length())
                             || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) {
                         obj = this.map.get(pattern);
                         bestMatch = pattern;
@@ -113,18 +117,18 @@ public class UriPatternMatcher<T> {
     /**
      * Tests if the given request path matches the given pattern.
      *
-     * @param pattern the pattern
-     * @param path the request path
-     * @return {@code true} if the request URI matches the pattern,
-     *   {@code false} otherwise.
+     * @param pattern
+     *            the pattern
+     * @param path
+     *            the request path
+     * @return {@code true} if the request URI matches the pattern, {@code false} otherwise.
      */
     protected boolean matchUriRequestPattern(final String pattern, final String path) {
         if (pattern.equals("*")) {
             return true;
         }
-        return
-        (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) ||
-        (pattern.startsWith("*") && path.endsWith(pattern.substring(1, pattern.length())));
+        return (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1)))
+                || (pattern.startsWith("*") && path.endsWith(pattern.substring(1, pattern.length())));
     }
 
     @Override

Added: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java?rev=1791744&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/UriRegexMatcher.java Tue Apr 18 05:45:40 2017
@@ -0,0 +1,119 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.protocol;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * Maintains a map of objects keyed by a request URI regular expression.
+ *
+ * <p>
+ * The insertion order is in maintained in that map such that the lookup tests each regex until there is a match. This
+ * class can be used to resolve an object matching a particular request URI.
+ * </p>
+ *
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.SAFE)
+public class UriRegexMatcher<T> implements LookupRegistry<T> {
+
+    private final Map<String, T> objectMap;
+    private final Map<String, Pattern> patternMap;
+
+    public UriRegexMatcher() {
+        super();
+        this.objectMap = new LinkedHashMap<>();
+        this.patternMap = new LinkedHashMap<>();
+    }
+
+    /**
+     * Registers the given object for URIs matching the given regex.
+     *
+     * @param regex
+     *            the regex to register the handler for.
+     * @param obj
+     *            the object.
+     */
+    @Override
+    public synchronized void register(final String regex, final T obj) {
+        Args.notNull(regex, "URI request regex");
+        this.objectMap.put(regex, obj);
+        this.patternMap.put(regex, Pattern.compile(regex));
+    }
+
+    /**
+     * Removes registered object, if exists, for the given regex.
+     *
+     * @param regex
+     *            the regex to unregister.
+     */
+    @Override
+    public synchronized void unregister(final String regex) {
+        if (regex == null) {
+            return;
+        }
+        this.objectMap.remove(regex);
+        this.patternMap.remove(regex);
+    }
+
+    /**
+     * Looks up an object matching the given request path.
+     *
+     * @param path
+     *            the request path
+     * @return object or {@code null} if no match is found.
+     */
+    @Override
+    public synchronized T lookup(final String path) {
+        Args.notNull(path, "Request path");
+        // direct match?
+        final T obj = this.objectMap.get(path);
+        if (obj == null) {
+            // regex match?
+            for (final Entry<String, Pattern> entry : this.patternMap.entrySet()) {
+                if (entry.getValue().matcher(path).matches()) {
+                    return objectMap.get(entry.getKey());
+                }
+            }
+        }
+        return obj;
+    }
+
+    @Override
+    public String toString() {
+        return this.objectMap.toString();
+    }
+
+}

Modified: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java?rev=1791744&r1=1791743&r2=1791744&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriPatternMatcher.java Tue Apr 18 05:45:40 2017
@@ -38,7 +38,7 @@ public class TestUriPatternMatcher {
         final Object h2 = new Object();
         final Object h3 = new Object();
 
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register("/h1", h1);
         matcher.register("/h2", h2);
         matcher.register("/h3", h3);
@@ -62,7 +62,7 @@ public class TestUriPatternMatcher {
 
     @Test(expected=IllegalArgumentException.class)
     public void testRegisterNull() throws Exception {
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register(null, null);
     }
 
@@ -73,7 +73,7 @@ public class TestUriPatternMatcher {
         final Object h3 = new Object();
         final Object def = new Object();
 
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register("*", def);
         matcher.register("/one/*", h1);
         matcher.register("/one/two/*", h2);
@@ -104,7 +104,7 @@ public class TestUriPatternMatcher {
         final Object h2 = new Object();
         final Object def = new Object();
 
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register("*", def);
         matcher.register("*.view", h1);
         matcher.register("*.form", h2);
@@ -129,7 +129,7 @@ public class TestUriPatternMatcher {
         final Object h1 = new Object();
         final Object h2 = new Object();
 
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register("/ma*", h1);
         matcher.register("*tch", h2);
 
@@ -140,13 +140,13 @@ public class TestUriPatternMatcher {
 
     @Test(expected=IllegalArgumentException.class)
     public void testRegisterInvalidInput() throws Exception {
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.register(null, null);
     }
 
     @Test(expected=IllegalArgumentException.class)
     public void testLookupInvalidInput() throws Exception {
-        final UriPatternMatcher<Object> matcher = new UriPatternMatcher<>();
+        final LookupRegistry<Object> matcher = new UriPatternMatcher<>();
         matcher.lookup(null);
     }
 

Added: httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java?rev=1791744&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java (added)
+++ httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestUriRegexMatcher.java Tue Apr 18 05:45:40 2017
@@ -0,0 +1,211 @@
+/*
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.protocol;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestUriRegexMatcher {
+
+    @Test
+    public void testRegisterUnregister() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+        final Object h3 = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register("/h1", h1);
+        matcher.register("/h2", h2);
+        matcher.register("/h3", h3);
+
+        Object h;
+
+        h = matcher.lookup("/h1");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h1 == h);
+        h = matcher.lookup("/h2");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h2 == h);
+        h = matcher.lookup("/h3");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h3 == h);
+
+        matcher.unregister("/h1");
+        h = matcher.lookup("/h1");
+        Assert.assertNull(h);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterNull() throws Exception {
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register(null, null);
+    }
+
+    @Test
+    public void testWildCardMatching1a() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+        final Object h3 = new Object();
+        final Object def = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register(".*", def);
+        matcher.register("/one/.*", h1);
+        matcher.register("/one/two/.*", h2);
+        matcher.register("/one/two/three/.*", h3);
+
+        Object h;
+
+        h = matcher.lookup("/one/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+
+        h = matcher.lookup("/one/two/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+
+        h = matcher.lookup("/one/two/three/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+
+        h = matcher.lookup("default/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+    }
+
+    @Test
+    public void testWildCardMatching1b() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+        final Object h3 = new Object();
+        final Object def = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register("/one/two/three/.*", h3);
+        matcher.register("/one/two/.*", h2);
+        matcher.register("/one/.*", h1);
+        matcher.register(".*", def);
+
+        Object h;
+
+        h = matcher.lookup("/one/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h1 == h);
+
+        h = matcher.lookup("/one/two/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h2 == h);
+
+        h = matcher.lookup("/one/two/three/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h3 == h);
+
+        h = matcher.lookup("default/request");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+    }
+
+    @Test
+    public void testWildCardMatching2a() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+        final Object def = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register(".*", def);
+        matcher.register(".*\\.view", h1);
+        matcher.register(".*\\.form", h2);
+
+        Object h;
+
+        h = matcher.lookup("/that.view");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+
+        h = matcher.lookup("/that.form");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+
+        h = matcher.lookup("/whatever");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+    }
+
+    @Test
+    public void testWildCardMatching2b() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+        final Object def = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register(".*\\.form", h2);
+        matcher.register(".*\\.view", h1);
+        matcher.register(".*", def);
+
+        Object h;
+
+        h = matcher.lookup("/that.view");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h1 == h);
+
+        h = matcher.lookup("/that.form");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h2 == h);
+
+        h = matcher.lookup("/whatever");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(def == h);
+    }
+
+    @Test
+    public void testSuffixPatternOverPrefixPatternMatch() throws Exception {
+        final Object h1 = new Object();
+        final Object h2 = new Object();
+
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register("/ma.*", h1);
+        matcher.register(".*tch", h2);
+
+        final Object h = matcher.lookup("/match");
+        Assert.assertNotNull(h);
+        Assert.assertTrue(h1 == h);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegisterInvalidInput() throws Exception {
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.register(null, null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testLookupInvalidInput() throws Exception {
+        final LookupRegistry<Object> matcher = new UriRegexMatcher<>();
+        matcher.lookup(null);
+    }
+
+}