CAMEL-6327: More work on new camel-netty-http component.

Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/cf1273af
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/cf1273af
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/cf1273af

Branch: refs/heads/master
Commit: cf1273af032e28b187a99a9f525fc1db7eff461c
Parents: aac9428
Author: Claus Ibsen <davscl...@apache.org>
Authored: Tue Jun 18 13:02:43 2013 +0200
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Tue Jun 18 13:02:43 2013 +0200

----------------------------------------------------------------------
 .../netty/http/ContextPathMatcher.java          | 36 ++++++++++
 .../netty/http/DefaultContextPathMatcher.java   | 72 ++++++++++++++++++++
 .../netty/http/HttpServerBootstrapFactory.java  |  3 +-
 .../netty/http/NettyHttpComponent.java          |  1 -
 .../netty/http/NettyHttpConfiguration.java      |  9 +++
 .../HttpServerMultiplexChannelHandler.java      | 22 ++++--
 .../NettyHttpTwoRoutesMatchOnUriPrefixTest.java | 68 ++++++++++++++++++
 .../camel/component/netty/NettyConsumer.java    | 18 ++---
 8 files changed, 210 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ContextPathMatcher.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ContextPathMatcher.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ContextPathMatcher.java
new file mode 100644
index 0000000..030fb9f
--- /dev/null
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/ContextPathMatcher.java
@@ -0,0 +1,36 @@
+/**
+ * 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.camel.component.netty.http;
+
+/**
+ * A matcher used for selecting the correct {@link 
org.apache.camel.component.netty.http.handlers.HttpServerChannelHandler}
+ * to handle an incoming {@link 
org.jboss.netty.handler.codec.http.HttpRequest} when you use multiple routes on 
the same
+ * port.
+ * <p/>
+ * As when we do that, we need to multiplex and select the correct consumer 
route to process the HTTP request.
+ * To do that we need to match on the incoming HTTP request context-path from 
the request.
+ */
+public interface ContextPathMatcher {
+
+    /**
+     * Whether the target context-path matches.
+     *
+     * @param target  the context-path from the incoming HTTP request
+     * @return <tt>true</tt> to match, <tt>false</tt> if not.
+     */
+    boolean matches(String target);
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultContextPathMatcher.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultContextPathMatcher.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultContextPathMatcher.java
new file mode 100644
index 0000000..55bfb58
--- /dev/null
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultContextPathMatcher.java
@@ -0,0 +1,72 @@
+/**
+ * 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.camel.component.netty.http;
+
+import java.util.Locale;
+
+/**
+ * A default {@link ContextPathMatcher} which supports the 
<tt>matchOnUriPrefix</tt> option.
+ */
+public class DefaultContextPathMatcher implements ContextPathMatcher {
+
+    private final String path;
+    private final boolean matchOnUriPrefix;
+
+    public DefaultContextPathMatcher(String path, boolean matchOnUriPrefix) {
+        this.path = path.toLowerCase(Locale.US);
+        this.matchOnUriPrefix = matchOnUriPrefix;
+    }
+
+    public boolean matches(String target) {
+        target = target.toLowerCase(Locale.US);
+        if (!matchOnUriPrefix) {
+            // exact match
+            return target.equals(path);
+        } else {
+            // match on prefix, then we just need to match the start of the 
context-path
+            return target.startsWith(path);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultContextPathMatcher that = (DefaultContextPathMatcher) o;
+
+        if (matchOnUriPrefix != that.matchOnUriPrefix) {
+            return false;
+        }
+        if (!path.equals(that.path)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = path.hashCode();
+        result = 31 * result + (matchOnUriPrefix ? 1 : 0);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerBootstrapFactory.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerBootstrapFactory.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerBootstrapFactory.java
index d004d96..223ccdb 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerBootstrapFactory.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerBootstrapFactory.java
@@ -73,8 +73,7 @@ public class HttpServerBootstrapFactory extends 
SingleTCPNettyServerBootstrapFac
             LOG.debug("BootstrapFactory on port {} is stopping", port);
             super.stop();
         } else {
-            LOG.debug("BootstrapFactory on port {} has {} active consumers, so 
cannot stop {} yet.",
-                    new Object[]{port, consumers, 
HttpServerBootstrapFactory.class.getName()});
+            LOG.debug("BootstrapFactory on port {} has {} registered 
consumers, so cannot stop yet.", port, consumers);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
index 8dfe774..6d8183b 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpComponent.java
@@ -37,7 +37,6 @@ public class NettyHttpComponent extends NettyComponent 
implements HeaderFilterSt
     // TODO: support on consumer
     // - validate routes on same port cannot have different SSL etc
     // - bridgeEndpoint
-    // - matchOnUriPrefix
     // - urlrewrite
 
     private final Map<Integer, HttpServerMultiplexChannelHandler> 
multiplexChannelHandlers = new HashMap<Integer, 
HttpServerMultiplexChannelHandler>();

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java
index a89ef96..cc71f9f 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java
@@ -34,6 +34,7 @@ public class NettyHttpConfiguration extends 
NettyConfiguration {
     private boolean compression;
     private boolean throwExceptionOnFailure = true;
     private boolean transferException;
+    private boolean matchOnUriPrefix;
     private String path;
 
     public NettyHttpConfiguration() {
@@ -108,6 +109,14 @@ public class NettyHttpConfiguration extends 
NettyConfiguration {
         this.mapHeaders = mapHeaders;
     }
 
+    public boolean isMatchOnUriPrefix() {
+        return matchOnUriPrefix;
+    }
+
+    public void setMatchOnUriPrefix(boolean matchOnUriPrefix) {
+        this.matchOnUriPrefix = matchOnUriPrefix;
+    }
+
     public String getPath() {
         return path;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java
index 3760d2a..e00faf8 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java
@@ -16,9 +16,12 @@
  */
 package org.apache.camel.component.netty.http.handlers;
 
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.camel.component.netty.http.ContextPathMatcher;
+import org.apache.camel.component.netty.http.DefaultContextPathMatcher;
 import org.apache.camel.component.netty.http.NettyHttpConsumer;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.ExceptionEvent;
@@ -30,7 +33,6 @@ import org.jboss.netty.handler.codec.http.HttpResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
 import static 
org.jboss.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE;
 import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
 
@@ -43,7 +45,7 @@ public class HttpServerMultiplexChannelHandler extends 
SimpleChannelUpstreamHand
 
     // use NettyHttpConsumer as logger to make it easier to read the logs as 
this is part of the consumer
     private static final transient Logger LOG = 
LoggerFactory.getLogger(NettyHttpConsumer.class);
-    private final ConcurrentMap<String, HttpServerChannelHandler> consumers = 
new ConcurrentHashMap<String, HttpServerChannelHandler>();
+    private final ConcurrentMap<ContextPathMatcher, HttpServerChannelHandler> 
consumers = new ConcurrentHashMap<ContextPathMatcher, 
HttpServerChannelHandler>();
     private final String token;
     private final int len;
 
@@ -54,12 +56,14 @@ public class HttpServerMultiplexChannelHandler extends 
SimpleChannelUpstreamHand
 
     public void addConsumer(NettyHttpConsumer consumer) {
         String path = pathAsKey(consumer.getConfiguration().getPath());
-        consumers.put(path, new HttpServerChannelHandler(consumer));
+        ContextPathMatcher matcher = new DefaultContextPathMatcher(path, 
consumer.getConfiguration().isMatchOnUriPrefix());
+        consumers.put(matcher, new HttpServerChannelHandler(consumer));
     }
 
     public void removeConsumer(NettyHttpConsumer consumer) {
         String path = pathAsKey(consumer.getConfiguration().getPath());
-        consumers.remove(path);
+        ContextPathMatcher matcher = new DefaultContextPathMatcher(path, 
consumer.getConfiguration().isMatchOnUriPrefix());
+        consumers.remove(matcher);
     }
 
     /**
@@ -106,12 +110,16 @@ public class HttpServerMultiplexChannelHandler extends 
SimpleChannelUpstreamHand
             path = path.substring(idx + len);
         }
 
-        // TODO: support matchOnUriPrefix
-
         // use the path as key to find the consumer handler to use
         path = pathAsKey(path);
 
-        return consumers.get(path);
+        // find the one that matches
+        for (Map.Entry<ContextPathMatcher, HttpServerChannelHandler> entry : 
consumers.entrySet()) {
+            if (entry.getKey().matches(path)) {
+                return entry.getValue();
+            }
+        }
+        return null;
     }
 
     private static String pathAsKey(String path) {

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java
 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java
new file mode 100644
index 0000000..382e2c9
--- /dev/null
+++ 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java
@@ -0,0 +1,68 @@
+/**
+ * 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.camel.component.netty.http;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Test;
+
+public class NettyHttpTwoRoutesMatchOnUriPrefixTest extends BaseNettyTest {
+
+    @Test
+    public void testTwoRoutesMatchOnUriPrefix() throws Exception {
+        getMockEndpoint("mock:foo").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:bar").expectedBodiesReceived("Hello Camel", "Hi 
Camel");
+
+        String out = 
template.requestBody("netty-http:http://localhost:{{port}}/foo";, "Hello World", 
String.class);
+        assertEquals("Bye World", out);
+
+        // the foo is not match on prefix so we cannot do /foo/beer
+        try {
+            
template.requestBody("netty-http:http://localhost:{{port}}/foo/beer";, "Hello 
World", String.class);
+            fail("Should have thrown exception");
+        } catch (CamelExecutionException e) {
+            NettyHttpOperationFailedException cause = 
assertIsInstanceOf(NettyHttpOperationFailedException.class, e.getCause());
+            assertEquals(503, cause.getStatusCode());
+        }
+
+        out = template.requestBody("netty-http:http://localhost:{{port}}/bar";, 
"Hello Camel", String.class);
+        assertEquals("Bye Camel", out);
+
+        // the bar is match on prefix so we can do /bar/beer
+        out = 
template.requestBody("netty-http:http://localhost:{{port}}/bar/beer";, "Hi 
Camel", String.class);
+        assertEquals("Bye Camel", out);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("netty-http:http://0.0.0.0:{{port}}/foo";)
+                    .to("mock:foo")
+                    .transform().constant("Bye World");
+
+                
from("netty-http:http://0.0.0.0:{{port}}/bar?matchOnUriPrefix=true";)
+                    .to("mock:bar")
+                    .transform().constant("Bye Camel");
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/cf1273af/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConsumer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConsumer.java
 
b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConsumer.java
index 6234cba..6435b40 100644
--- 
a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConsumer.java
+++ 
b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConsumer.java
@@ -47,16 +47,16 @@ public class NettyConsumer extends DefaultConsumer {
 
         LOG.debug("Netty consumer binding to: {}", configuration.getAddress());
 
-        // setup pipeline factory
-        ServerPipelineFactory pipelineFactory;
-        ServerPipelineFactory factory = 
configuration.getServerPipelineFactory();
-        if (factory != null) {
-            pipelineFactory = factory.createPipelineFactory(this);
-        } else {
-            pipelineFactory = new DefaultServerPipelineFactory(this);
-        }
-
         if (nettyServerBootstrapFactory == null) {
+            // setup pipeline factory
+            ServerPipelineFactory pipelineFactory;
+            ServerPipelineFactory factory = 
configuration.getServerPipelineFactory();
+            if (factory != null) {
+                pipelineFactory = factory.createPipelineFactory(this);
+            } else {
+                pipelineFactory = new DefaultServerPipelineFactory(this);
+            }
+
             if (isTcp()) {
                 nettyServerBootstrapFactory = new 
SingleTCPNettyServerBootstrapFactory();
             } else {

Reply via email to