Repository: camel Updated Branches: refs/heads/master a2a569630 -> 6e88e7a27
CAMEL-9678 camel-undertow - Keep restarting server when add/remove routes Refactored camel-undertow consumer to enable hot swapping individual handlers which correspond to each consumers without restarting undertow server. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/6e88e7a2 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/6e88e7a2 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/6e88e7a2 Branch: refs/heads/master Commit: 6e88e7a27bfed8910d736f664281e67236573988 Parents: a2a5696 Author: Tomohisa Igarashi <tm.igara...@gmail.com> Authored: Wed Oct 26 17:17:13 2016 +0900 Committer: Andrea Cosentino <anco...@gmail.com> Committed: Wed Oct 26 11:20:55 2016 +0200 ---------------------------------------------------------------------- .../component/undertow/DefaultUndertowHost.java | 100 +++++++++++ .../component/undertow/UndertowComponent.java | 98 +++------- .../component/undertow/UndertowConsumer.java | 118 +++++++----- .../undertow/UndertowConsumerResolver.java | 81 --------- .../component/undertow/UndertowHostKey.java | 68 +++++++ .../component/undertow/UndertowRegistry.java | 115 ------------ .../undertow/handlers/CamelMethodHandler.java | 96 ++++++++++ .../undertow/handlers/CamelPathHandler.java | 84 +++++++++ .../handlers/CamelPathTemplateHandler.java | 99 ++++++++++ .../undertow/handlers/CamelRootHandler.java | 179 +++++++++++++++++++ .../undertow/handlers/HttpCamelHandler.java | 135 -------------- .../UndertowConsumerUnregisterTest.java | 38 ++++ .../src/test/resources/log4j2.properties | 4 + 13 files changed, 761 insertions(+), 454 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHost.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHost.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHost.java new file mode 100644 index 0000000..7db4c99 --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/DefaultUndertowHost.java @@ -0,0 +1,100 @@ +/** + * 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.undertow; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import io.undertow.Handlers; +import io.undertow.Undertow; +import io.undertow.server.HttpHandler; +import io.undertow.server.handlers.PathHandler; +import io.undertow.server.handlers.PathTemplateHandler; +import io.undertow.util.PathTemplate; +import org.apache.camel.component.undertow.handlers.CamelRootHandler; +import org.apache.camel.component.undertow.handlers.NotFoundHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The default UndertowHost which manages standalone Undertow server. + */ +public class DefaultUndertowHost implements UndertowHost { + private static final Logger LOG = LoggerFactory.getLogger(DefaultUndertowHost.class); + + private UndertowHostKey key; + private CamelRootHandler rootHandler; + private Undertow undertow; + private String hostString; + + public DefaultUndertowHost(UndertowHostKey key) { + this.key = key; + rootHandler = new CamelRootHandler(new NotFoundHandler()); + } + + @Override + public void validateEndpointURI(URI httpURI) { + // all URIs are good + } + + @Override + public synchronized void registerHandler(HttpHandlerRegistrationInfo registrationInfo, HttpHandler handler) { + if (undertow == null) { + Undertow.Builder builder = Undertow.builder(); + if (key.getSslContext() != null) { + builder.addHttpsListener(key.getPort(), key.getHost(), key.getSslContext()); + } else { + builder.addHttpListener(key.getPort(), key.getHost()); + } + + undertow = builder.setHandler(rootHandler).build(); + LOG.info("Starting Undertow server on {}://{}:{}", key.getSslContext() != null ? "https" : "http", key.getHost(), key.getPort()); + undertow.start(); + } + + String path = registrationInfo.getUri().getPath(); + String methods = registrationInfo.getMethodRestrict(); + boolean prefixMatch = registrationInfo.isMatchOnUriPrefix(); + rootHandler.add(path, methods != null ? methods.split(",") : null, prefixMatch, handler); + } + + @Override + public synchronized void unregisterHandler(HttpHandlerRegistrationInfo registrationInfo) { + if (undertow == null) { + return; + } + + String path = registrationInfo.getUri().getPath(); + String methods = registrationInfo.getMethodRestrict(); + boolean prefixMatch = registrationInfo.isMatchOnUriPrefix(); + rootHandler.remove(path, methods != null ? methods.split(",") : null, prefixMatch); + + if (rootHandler.isEmpty()) { + LOG.info("Stopping Undertow server on {}://{}:{}", key.getSslContext() != null ? "https" : "http", key.getHost(), key.getPort()); + undertow.stop(); + undertow = null; + } + } + + public String toString() { + if (hostString == null) { + hostString = String.format("DefaultUndertowHost[%s://%s:%s]", key.getSslContext() != null ? "https" : "http", key.getHost(), key.getPort()); + } + return hostString; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java index f2b78c3..d757070 100644 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowComponent.java @@ -23,6 +23,9 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.net.ssl.SSLContext; import io.undertow.Handlers; import io.undertow.Undertow; @@ -30,15 +33,16 @@ import io.undertow.attribute.ExchangeAttributes; import io.undertow.predicate.PathTemplatePredicate; import io.undertow.predicate.Predicate; import io.undertow.predicate.Predicates; +import io.undertow.server.HttpHandler; import io.undertow.server.handlers.PathHandler; +import io.undertow.server.handlers.PathTemplateHandler; import io.undertow.server.handlers.PredicateHandler; - +import io.undertow.util.PathTemplate; import org.apache.camel.CamelContext; import org.apache.camel.Consumer; import org.apache.camel.Endpoint; import org.apache.camel.Processor; import org.apache.camel.Producer; -import org.apache.camel.component.undertow.handlers.HttpCamelHandler; import org.apache.camel.component.undertow.handlers.NotFoundHandler; import org.apache.camel.impl.UriEndpointComponent; import org.apache.camel.spi.RestApiConsumerFactory; @@ -63,7 +67,7 @@ public class UndertowComponent extends UriEndpointComponent implements RestConsu private static final Logger LOG = LoggerFactory.getLogger(UndertowEndpoint.class); private UndertowHttpBinding undertowHttpBinding; - private final Map<Integer, UndertowRegistry> serversRegistry = new HashMap<Integer, UndertowRegistry>(); + private Map<UndertowHostKey, UndertowHost> undertowRegistry = new ConcurrentHashMap<UndertowHostKey, UndertowHost>(); private SSLContextParameters sslContextParameters; public UndertowComponent() { @@ -275,86 +279,26 @@ public class UndertowComponent extends UriEndpointComponent implements RestConsu } public void registerConsumer(UndertowConsumer consumer) { - int port = consumer.getEndpoint().getHttpURI().getPort(); - if (serversRegistry.containsKey(port)) { - UndertowRegistry undertowRegistry = serversRegistry.get(port); - undertowRegistry.registerConsumer(consumer); - } else { - // Create a new server to listen on the specified port - serversRegistry.put(port, new UndertowRegistry(consumer, port)); + URI uri = consumer.getEndpoint().getHttpURI(); + UndertowHostKey key = new UndertowHostKey(uri.getHost(), uri.getPort(), consumer.getEndpoint().getSslContext()); + UndertowHost host = undertowRegistry.get(key); + if (host == null) { + host = createUndertowHost(key); + undertowRegistry.put(key, host); } + host.validateEndpointURI(uri); + host.registerHandler(consumer.getHttpHandlerRegistrationInfo(), consumer); } public void unregisterConsumer(UndertowConsumer consumer) { - int port = consumer.getEndpoint().getHttpURI().getPort(); - if (serversRegistry.containsKey(port)) { - UndertowRegistry undertowRegistry = serversRegistry.get(port); - undertowRegistry.unregisterConsumer(consumer); - - if (undertowRegistry.isEmpty()) { - // If there are no consumers left, we can shut down the server - Undertow server = undertowRegistry.getServer(); - if (server != null) { - server.stop(); - } - serversRegistry.remove(port); - } else { - // Else, rebuild the server - startServer(consumer); - } - } + URI uri = consumer.getEndpoint().getHttpURI(); + UndertowHostKey key = new UndertowHostKey(uri.getHost(), uri.getPort(), consumer.getEndpoint().getSslContext()); + UndertowHost host = undertowRegistry.get(key); + host.unregisterHandler(consumer.getHttpHandlerRegistrationInfo()); } - public void startServer(UndertowConsumer consumer) { - int port = consumer.getEndpoint().getHttpURI().getPort(); - LOG.info("Starting server on port: {}", port); - UndertowRegistry undertowRegistry = serversRegistry.get(port); - if (undertowRegistry.getServer() != null) { - //server is running, we need to stop it first and then rebuild - undertowRegistry.getServer().stop(); - } - Undertow newServer = rebuildServer(undertowRegistry); - newServer.start(); - undertowRegistry.setServer(newServer); - } - - protected Undertow rebuildServer(UndertowRegistry registry) { - Undertow.Builder result = Undertow.builder(); - if (registry.getSslContext() != null) { - result = result.addHttpsListener(registry.getPort(), registry.getHost(), registry.getSslContext()); - } else { - result = result.addHttpListener(registry.getPort(), registry.getHost()); - } - - PathHandler pathHandler = Handlers.path(new NotFoundHandler()); - HttpCamelHandler handler = new HttpCamelHandler(); - List<Predicate> predicates = new ArrayList<Predicate>(); - for (String key : registry.getConsumersRegistry().keySet()) { - UndertowConsumer consumer = registry.getConsumersRegistry().get(key); - UndertowEndpoint endpoint = consumer.getEndpoint(); - String path = endpoint.getHttpURI().getPath(); - - // Assume URI contains REST variables - if (path.contains("{")) { - predicates.add(new PathTemplatePredicate(path, ExchangeAttributes.relativePath())); - } else { - if (endpoint.getMatchOnUriPrefix()) { - predicates.add(Predicates.prefix(path)); - } else { - predicates.add(Predicates.path(path)); - } - } - - handler.connectConsumer(consumer); - - LOG.debug("Rebuild for pathHandler: {}", path); - } - - Predicate combinedPathPredicate = Predicates.or(predicates.toArray(new Predicate[0])); - pathHandler.addPrefixPath("/", new PredicateHandler(combinedPathPredicate, handler, new NotFoundHandler())); - - result = result.setHandler(pathHandler); - return result.build(); + protected UndertowHost createUndertowHost(UndertowHostKey key) { + return new DefaultUndertowHost(key); } public UndertowHttpBinding getUndertowHttpBinding() { http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java index 49efe0a..6a80892 100644 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumer.java @@ -16,24 +16,30 @@ */ package org.apache.camel.component.undertow; -import java.net.URI; +import java.io.IOException; +import java.nio.ByteBuffer; import io.undertow.server.HttpHandler; - +import io.undertow.server.HttpServerExchange; +import io.undertow.util.Headers; +import io.undertow.util.HttpString; +import io.undertow.util.Methods; +import io.undertow.util.MimeMappings; +import io.undertow.util.StatusCodes; +import org.apache.camel.Exchange; import org.apache.camel.Processor; -import org.apache.camel.component.undertow.handlers.HttpCamelHandler; +import org.apache.camel.TypeConverter; import org.apache.camel.impl.DefaultConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The Undertow consumer. + * The Undertow consumer which is also an Undertow HttpHandler implementation to handle incoming request. */ -public class UndertowConsumer extends DefaultConsumer { +public class UndertowConsumer extends DefaultConsumer implements HttpHandler { private static final Logger LOG = LoggerFactory.getLogger(UndertowConsumer.class); - private UndertowHost undertowHost; private HttpHandlerRegistrationInfo registrationInfo; public UndertowConsumer(UndertowEndpoint endpoint, Processor processor) { @@ -45,46 +51,19 @@ public class UndertowConsumer extends DefaultConsumer { return (UndertowEndpoint) super.getEndpoint(); } - public UndertowHost getUndertowHost() { - if (undertowHost == null) { - undertowHost = createUndertowHost(); - } - return undertowHost; - } - - protected UndertowHost createUndertowHost() { - return new DefaultUndertowHost(); - } - @Override protected void doStart() throws Exception { super.doStart(); - LOG.debug("Undertow consumer is starting"); getEndpoint().getComponent().registerConsumer(this); - - UndertowHost host = getUndertowHost(); - - HttpCamelHandler httpCamelHandler = new HttpCamelHandler(); - httpCamelHandler.connectConsumer(this); - - HttpHandlerRegistrationInfo registrationInfo = getHttpHandlerRegistrationInfo(); - - host.validateEndpointURI(registrationInfo.getUri()); - host.registerHandler(registrationInfo, httpCamelHandler); } @Override - protected void doStop() { - LOG.debug("Undertow consumer is stopping"); - - HttpHandlerRegistrationInfo registrationInfo = getHttpHandlerRegistrationInfo(); - UndertowHost host = getUndertowHost(); - host.unregisterHandler(registrationInfo); - + protected void doStop() throws Exception { + super.doStop(); getEndpoint().getComponent().unregisterConsumer(this); } - private HttpHandlerRegistrationInfo getHttpHandlerRegistrationInfo() { + public HttpHandlerRegistrationInfo getHttpHandlerRegistrationInfo() { if (registrationInfo == null) { UndertowEndpoint endpoint = getEndpoint(); @@ -96,21 +75,68 @@ public class UndertowConsumer extends DefaultConsumer { return registrationInfo; } - class DefaultUndertowHost implements UndertowHost { + @Override + public void handleRequest(HttpServerExchange httpExchange) throws Exception { + HttpString requestMethod = httpExchange.getRequestMethod(); + + if (Methods.OPTIONS.equals(requestMethod) && !getEndpoint().isOptionsEnabled()) { + String allowedMethods; + if (getEndpoint().getHttpMethodRestrict() != null) { + allowedMethods = "OPTIONS," + getEndpoint().getHttpMethodRestrict(); + } else { + allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH"; + } + //return list of allowed methods in response headers + httpExchange.setStatusCode(StatusCodes.OK); + httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); + httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_LENGTH, 0); + httpExchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods); + httpExchange.getResponseSender().close(); + return; + } - @Override - public void validateEndpointURI(URI httpURI) { - // all URIs are good + //perform blocking operation on exchange + if (httpExchange.isInIoThread()) { + httpExchange.dispatch(this); + return; } - @Override - public void registerHandler(HttpHandlerRegistrationInfo registrationInfo, HttpHandler handler) { - getEndpoint().getComponent().startServer(UndertowConsumer.this); + //create new Exchange + //binding is used to extract header and payload(if available) + Exchange camelExchange = getEndpoint().createExchange(httpExchange); + + //Unit of Work to process the Exchange + createUoW(camelExchange); + try { + getProcessor().process(camelExchange); + } catch (Exception e) { + getExceptionHandler().handleException(e); + } finally { + doneUoW(camelExchange); } - @Override - public void unregisterHandler(HttpHandlerRegistrationInfo registrationInfo) { - // do nothing + Object body = getResponseBody(httpExchange, camelExchange); + TypeConverter tc = getEndpoint().getCamelContext().getTypeConverter(); + + if (body == null) { + LOG.trace("No payload to send as reply for exchange: " + camelExchange); + httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); + httpExchange.getResponseSender().send("No response available"); + } else { + ByteBuffer bodyAsByteBuffer = tc.convertTo(ByteBuffer.class, body); + httpExchange.getResponseSender().send(bodyAsByteBuffer); } + httpExchange.getResponseSender().close(); } + + private Object getResponseBody(HttpServerExchange httpExchange, Exchange camelExchange) throws IOException { + Object result; + if (camelExchange.hasOut()) { + result = getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getOut()); + } else { + result = getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getIn()); + } + return result; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumerResolver.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumerResolver.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumerResolver.java deleted file mode 100644 index 9a56c2a..0000000 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowConsumerResolver.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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.undertow; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import io.undertow.server.HttpServerExchange; -import io.undertow.util.HttpString; -import org.apache.camel.support.RestConsumerContextPathMatcher; - -public class UndertowConsumerResolver { - - public UndertowConsumer resolve(HttpServerExchange exchange, Map<String, UndertowConsumer> consumers) { - UndertowConsumer answer = null; - - String path = exchange.getRequestPath(); - if (path == null) { - return null; - } - HttpString method = exchange.getRequestMethod(); - if (method == null) { - return null; - } - - List<RestConsumerContextPathMatcher.ConsumerPath> paths = new ArrayList<RestConsumerContextPathMatcher.ConsumerPath>(); - for (final Map.Entry<String, UndertowConsumer> entry : consumers.entrySet()) { - paths.add(new RestConsumerContextPathMatcher.ConsumerPath<UndertowConsumer>() { - @Override - public String getRestrictMethod() { - return entry.getValue().getEndpoint().getHttpMethodRestrict(); - } - - @Override - public String getConsumerPath() { - return entry.getValue().getEndpoint().getHttpURI().getPath(); - } - - @Override - public UndertowConsumer getConsumer() { - return entry.getValue(); - } - }); - } - - RestConsumerContextPathMatcher.ConsumerPath<UndertowConsumer> best = RestConsumerContextPathMatcher.matchBestPath(method.toString(), path, paths); - if (best != null) { - answer = best.getConsumer(); - } - - if (answer == null) { - for (String key : consumers.keySet()) { - String consumerPath = consumers.get(key).getEndpoint().getHttpURI().getPath(); - UndertowConsumer consumer = consumers.get(key); - boolean matchOnUriPrefix = consumer.getEndpoint().getMatchOnUriPrefix(); - if (RestConsumerContextPathMatcher.matchPath(path, consumerPath, matchOnUriPrefix)) { - answer = consumers.get(key); - break; - } - } - } - - return answer; - } -} - http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHostKey.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHostKey.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHostKey.java new file mode 100644 index 0000000..be64d09 --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHostKey.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.undertow; + +import javax.net.ssl.SSLContext; + +/** + * The key to identify an Undertow host. + */ +public final class UndertowHostKey { + private final String host; + private final int port; + private final SSLContext sslContext; + + public UndertowHostKey(String host, int port, SSLContext ssl) { + this.host = host; + this.port = port; + this.sslContext = ssl; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public SSLContext getSslContext() { + return sslContext; + } + + @Override + public boolean equals(Object target) { + if (!(target instanceof UndertowHostKey)) { + return false; + } + UndertowHostKey targetKey = (UndertowHostKey)target; + boolean answer = true; + if (this.sslContext != null || targetKey.sslContext != null) { + answer = this.sslContext != null && targetKey.sslContext != null + && this.sslContext.equals(targetKey.sslContext); + } + return answer && this.host != null && targetKey.host != null + && this.host.equals(targetKey.host) && this.port == targetKey.port; + } + + @Override + public int hashCode() { + int answer = host.hashCode(); + answer = answer * 31 + Integer.hashCode(port); + return answer * 31 + (sslContext != null ? sslContext.hashCode() : 0); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowRegistry.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowRegistry.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowRegistry.java deleted file mode 100644 index 6d7e434..0000000 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowRegistry.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * 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.undertow; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import javax.net.ssl.SSLContext; - -import io.undertow.Undertow; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class is used to hold Undertow instances during runtime. - * One of the benefits is reuse of same TCP port for more endpoints. - */ -public class UndertowRegistry { - - private static final Logger LOG = LoggerFactory.getLogger(UndertowRegistry.class); - - private int port; - private SSLContext sslContext; - private String host; - private Undertow server; - private Map<String, UndertowConsumer> consumersRegistry = new HashMap<String, UndertowConsumer>(); - - public UndertowRegistry(UndertowConsumer consumer, int port) { - registerConsumer(consumer); - this.port = port; - if (consumer.getEndpoint().getSslContext() != null) { - sslContext = consumer.getEndpoint().getSslContext(); - } - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public void registerConsumer(UndertowConsumer consumer) { - UndertowEndpoint endpoint = consumer.getEndpoint(); - URI httpUri = endpoint.getHttpURI(); - if (host != null && !host.equals(httpUri.getHost())) { - throw new IllegalArgumentException("Cannot register UndertowConsumer on different host and same port: {}" + host + " " + httpUri.getHost()); - } else { - host = httpUri.getHost(); - } - LOG.info("Adding consumer to consumerRegistry: {}", httpUri); - consumersRegistry.put(endpoint.getEndpointUri(), consumer); - if (sslContext != null && endpoint.getSslContext() != null) { - throw new IllegalArgumentException("Cannot register UndertowConsumer with different SSL config"); - } - - } - - public void unregisterConsumer(UndertowConsumer consumer) { - UndertowEndpoint endpoint = consumer.getEndpoint(); - String endpointUri = endpoint.getEndpointUri(); - if (consumersRegistry.containsKey(endpointUri)) { - consumersRegistry.remove(endpointUri); - } else { - LOG.debug("Cannot unregister consumer {} as it was not registered", consumer); - } - } - - public boolean isEmpty() { - return consumersRegistry.isEmpty(); - } - - public Undertow getServer() { - return server; - } - - public void setServer(Undertow server) { - this.server = server; - } - - public Map<String, UndertowConsumer> getConsumersRegistry() { - return consumersRegistry; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public SSLContext getSslContext() { - return sslContext; - } - - public void setSslContext(SSLContext sslContext) { - this.sslContext = sslContext; - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelMethodHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelMethodHandler.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelMethodHandler.java new file mode 100644 index 0000000..26584b5 --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelMethodHandler.java @@ -0,0 +1,96 @@ +/** + * 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.undertow.handlers; + +import java.util.HashMap; +import java.util.Map; + +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.MimeMappings; +import io.undertow.util.StatusCodes; +import org.apache.camel.component.undertow.ExchangeHeaders; + +/** + * A HttpHandler build a mapping between HTTP methods and handlers and dispatch requests along the map. + */ +public class CamelMethodHandler implements HttpHandler { + private Map<String, HttpHandler> methodMap = new HashMap<String, HttpHandler>(); + private HttpHandler defaultHandler; + private String handlerString; + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + HttpHandler handler = methodMap.get(exchange.getRequestMethod().toString()); + if (handler != null) { + handler.handleRequest(exchange); + } else if (defaultHandler != null) { + defaultHandler.handleRequest(exchange); + } else { + exchange.setStatusCode(StatusCodes.METHOD_NOT_ALLOWED); + exchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); + exchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_LENGTH, 0); + exchange.endExchange(); + } + } + + public synchronized void add(String[] methods, HttpHandler handler) { + Map<String, HttpHandler> adding = new HashMap<String, HttpHandler>(); + for (String method : methods) { + HttpHandler existing = methodMap.get(method); + if (methodMap.get(method) != null) { + throw new IllegalArgumentException(String.format( + "Duplicate handler for a method '%s': '%s', '%s'", method, existing, handler)); + } + adding.put(method, handler); + } + methodMap.putAll(adding); + handlerString = null; + } + + public synchronized void remove(String[] methods) { + for (String method : methods) { + methodMap.remove(method); + } + handlerString = null; + } + + public synchronized void addDefault(HttpHandler handler) { + if (defaultHandler != null) { + throw new IllegalArgumentException(String.format( + "Duplicate default handler: '%s', '%s'", defaultHandler, handler)); + } + defaultHandler = handler; + handlerString = null; + } + + public synchronized void removeDefault() { + defaultHandler = null; + handlerString = null; + } + + public boolean isEmpty() { + return defaultHandler == null && methodMap.isEmpty(); + } + + public String toString() { + if (handlerString == null) { + handlerString = "CamelMethodHandler[default=" + defaultHandler + ", " + methodMap + "]"; + } + return handlerString; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathHandler.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathHandler.java new file mode 100644 index 0000000..f9284c2 --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathHandler.java @@ -0,0 +1,84 @@ +/** + * 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.undertow.handlers; + +import java.util.HashMap; +import java.util.Map; + +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.PathHandler; + +/** + * Extended PathHandler to monitor add/remove handlers. + */ +public class CamelPathHandler extends PathHandler { + private Map<String, HttpHandler> handlers = new HashMap<String, HttpHandler>(); + private String handlerString; + + public CamelPathHandler(HttpHandler defaultHandler) { + super(defaultHandler); + } + + @Override + public synchronized PathHandler addPrefixPath(final String path, final HttpHandler handler) { + super.addPrefixPath(path, handler); + handlers.put(path, handler); + handlerString = null; + return this; + } + + @Override + public synchronized PathHandler addExactPath(final String path, final HttpHandler handler) { + super.addExactPath(path, handler); + handlers.put(path, handler); + handlerString = null; + return this; + } + + @Override + public synchronized PathHandler removePrefixPath(final String path) { + super.removePrefixPath(path); + handlers.remove(path); + handlerString = null; + return this; + } + + @Override + public synchronized PathHandler removeExactPath(final String path) { + super.removeExactPath(path); + handlers.remove(path); + handlerString = null; + return this; + } + + public HttpHandler getHandler(String path) { + return handlers.get(path); + } + + public boolean isEmpty() { + return handlers.isEmpty(); + } + + public String toString() { + if (handlerString == null) { + handlerString = "CamelPathHandler[" + handlers + "]"; + } + return handlerString; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathTemplateHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathTemplateHandler.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathTemplateHandler.java new file mode 100644 index 0000000..ff086da --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelPathTemplateHandler.java @@ -0,0 +1,99 @@ +/** + * 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.undertow.handlers; + +import java.util.HashMap; +import java.util.Map; + +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.PathTemplateHandler; + +/** + * Extended PathTemplateHandler to monitor add/remove handlers. Also this enables hot swapping a default handler. + */ +public class CamelPathTemplateHandler implements HttpHandler { + private Map<String, CamelMethodHandler> handlers = new HashMap<String, CamelMethodHandler>(); + private Wrapper defaultHandlerWrapper = new Wrapper(); + private PathTemplateHandler delegate; + private String handlerString; + + public CamelPathTemplateHandler(CamelMethodHandler defaultHandler) { + defaultHandlerWrapper.set(defaultHandler); + delegate = new PathTemplateHandler(defaultHandlerWrapper); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + delegate.handleRequest(exchange); + } + + public synchronized CamelPathTemplateHandler add(final String uriTemplate, final CamelMethodHandler handler) { + delegate.add(uriTemplate, handler); + handlers.put(uriTemplate, handler); + handlerString = null; + return this; + } + + public synchronized CamelPathTemplateHandler remove(final String uriTemplate) { + delegate.remove(uriTemplate); + handlers.remove(uriTemplate); + handlerString = null; + return this; + } + + public CamelMethodHandler get(final String uriTemplate) { + return handlers.get(uriTemplate); + } + + public boolean isEmpty() { + return handlers.isEmpty(); + } + + public CamelMethodHandler getDefault() { + return this.defaultHandlerWrapper.get(); + } + + public synchronized void setDefault(final CamelMethodHandler defaultHandler) { + this.defaultHandlerWrapper.set(defaultHandler); + handlerString = null; + } + + public String toString() { + if (handlerString == null) { + handlerString = "CamelPathTemplateHandler[default=" + defaultHandlerWrapper.get() + ", " + handlers + "]"; + } + return handlerString; + } + + class Wrapper implements HttpHandler { + private CamelMethodHandler handler; + + public void set(CamelMethodHandler handler) { + this.handler = handler; + } + + public CamelMethodHandler get() { + return this.handler; + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + handler.handleRequest(exchange); + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelRootHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelRootHandler.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelRootHandler.java new file mode 100644 index 0000000..a0a8ae3 --- /dev/null +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/CamelRootHandler.java @@ -0,0 +1,179 @@ +/** + * 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.undertow.handlers; + +import java.util.HashMap; +import java.util.Map; + +import io.undertow.Handlers; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.PathHandler; +import io.undertow.server.handlers.PathTemplateHandler; +import io.undertow.util.Headers; +import io.undertow.util.PathTemplate; + +/** + * Custom root handler to enable hot swapping individual handlers assigned for each path template and/or HTTP method. + */ +public class CamelRootHandler implements HttpHandler { + private CamelPathHandler pathHandler; + + public CamelRootHandler(HttpHandler defaultHandler) { + pathHandler = new CamelPathHandler(defaultHandler); + } + + public void handleRequest(HttpServerExchange exchange) throws Exception { + pathHandler.handleRequest(exchange); + } + + public synchronized void add(String path, String[] methods, boolean prefixMatch, HttpHandler handler) { + String basePath = getBasePath(path); + HttpHandler basePathHandler = pathHandler.getHandler(basePath); + + CamelMethodHandler targetHandler; + if (path.contains("{")) { + // Adding a handler for the template path + String relativePath = path.substring(basePath.length()); + if (basePathHandler instanceof CamelPathTemplateHandler) { + CamelPathTemplateHandler templateHandler = (CamelPathTemplateHandler)basePathHandler; + targetHandler = templateHandler.get(relativePath); + if (targetHandler == null) { + targetHandler = new CamelMethodHandler(); + templateHandler.add(relativePath, targetHandler); + } + } else { + CamelPathTemplateHandler templateHandler; + if (basePathHandler instanceof CamelMethodHandler) { + // A static path handler is already set for the base path. Use it as a default handler + templateHandler = new CamelPathTemplateHandler((CamelMethodHandler)basePathHandler); + } else if (basePathHandler == null) { + templateHandler = new CamelPathTemplateHandler(new CamelMethodHandler()); + } else { + throw new IllegalArgumentException(String.format("Unsupported handler '%s' was found", basePathHandler)); + } + targetHandler = new CamelMethodHandler(); + templateHandler.add(relativePath, targetHandler); + pathHandler.addPrefixPath(basePath, templateHandler); + } + + } else { + // Adding a handler for the static path + if (basePathHandler instanceof CamelPathTemplateHandler) { + CamelPathTemplateHandler templateHandler = (CamelPathTemplateHandler)basePathHandler; + if (prefixMatch) { + targetHandler = templateHandler.getDefault(); + } else { + throw new IllegalArgumentException(String.format("Duplicate handlers on a path '%s'", path)); + } + } else { + if (basePathHandler instanceof CamelMethodHandler) { + targetHandler = (CamelMethodHandler)basePathHandler; + } else if (basePathHandler == null) { + targetHandler = new CamelMethodHandler(); + if (prefixMatch) { + pathHandler.addPrefixPath(basePath, targetHandler); + } else { + pathHandler.addExactPath(basePath, targetHandler); + } + } else { + throw new IllegalArgumentException(String.format("Unsupported handler '%s' was found", basePathHandler)); + } + } + } + + if (methods != null && methods.length != 0) { + targetHandler.add(methods, handler); + } else { + targetHandler.addDefault(handler); + } + } + + public synchronized void remove(String path, String[] methods, boolean prefixMatch) { + String basePath = getBasePath(path); + HttpHandler basePathHandler = pathHandler.getHandler(basePath); + if (basePathHandler == null) { + return; + } + + if (path.contains("{")) { + // Removing a handler for the template path + String relativePath = path.substring(basePath.length()); + CamelPathTemplateHandler templateHandler = (CamelPathTemplateHandler)basePathHandler; + CamelMethodHandler targetHandler = templateHandler.get(relativePath); + if (methods != null && methods.length != 0) { + targetHandler.remove(methods); + } else { + targetHandler.removeDefault(); + } + if (targetHandler.isEmpty()) { + templateHandler.remove(relativePath); + if (templateHandler.isEmpty()) { + pathHandler.removePrefixPath(basePath); + } + } + + } else { + // Removing a handler for the static path + if (basePathHandler instanceof CamelPathTemplateHandler) { + String relativePath = path.substring(basePath.length()); + CamelPathTemplateHandler templateHandler = (CamelPathTemplateHandler)basePathHandler; + CamelMethodHandler targetHandler = templateHandler.getDefault(); + if (methods != null && methods.length != 0) { + targetHandler.remove(methods); + } else { + targetHandler.removeDefault(); + } + if (targetHandler.isEmpty()) { + templateHandler.remove(relativePath); + if (templateHandler.isEmpty()) { + pathHandler.removePrefixPath(basePath); + } + } + } else { + CamelMethodHandler targetHandler = (CamelMethodHandler)basePathHandler; + if (methods != null && methods.length != 0) { + targetHandler.remove(methods); + } else { + targetHandler.removeDefault(); + } + if (targetHandler.isEmpty()) { + if (prefixMatch) { + pathHandler.removePrefixPath(basePath); + } else { + pathHandler.removeExactPath(basePath); + } + } + } + } + } + + public synchronized boolean isEmpty() { + return pathHandler.isEmpty(); + } + + public String toString() { + return pathHandler.toString(); + } + + private String getBasePath(String path) { + if (path.contains("{")) { + return PathTemplate.create(path).getBase(); + } + return path; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java deleted file mode 100644 index d9778c5..0000000 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/handlers/HttpCamelHandler.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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.undertow.handlers; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import io.undertow.server.HttpHandler; -import io.undertow.server.HttpServerExchange; -import io.undertow.util.Headers; -import io.undertow.util.HttpString; -import io.undertow.util.Methods; -import io.undertow.util.MimeMappings; -import io.undertow.util.StatusCodes; -import org.apache.camel.Exchange; -import org.apache.camel.TypeConverter; -import org.apache.camel.component.undertow.ExchangeHeaders; -import org.apache.camel.component.undertow.UndertowConsumer; -import org.apache.camel.component.undertow.UndertowConsumerResolver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Custom handler to process incoming HTTP request and prepare them - * to be used in the Camel route. - * - * This class can be considered part of UndertowConsumer implementation. - */ -public class HttpCamelHandler implements HttpHandler { - private static final Logger LOG = LoggerFactory.getLogger(HttpCamelHandler.class); - private UndertowConsumerResolver resolver = new UndertowConsumerResolver(); - private ConcurrentMap<String, UndertowConsumer> consumers = new ConcurrentHashMap<String, UndertowConsumer>(); - - @Override - public void handleRequest(HttpServerExchange httpExchange) throws Exception { - UndertowConsumer consumer = resolver.resolve(httpExchange, consumers); - - if (consumer == null) { - LOG.debug("Unable to resolve consumer matching path {}", httpExchange.getRequestPath()); - new NotFoundHandler().handleRequest(httpExchange); - return; - } - - HttpString requestMethod = httpExchange.getRequestMethod(); - - if (Methods.OPTIONS.equals(requestMethod) && !consumer.getEndpoint().isOptionsEnabled()) { - String allowedMethods; - if (consumer.getEndpoint().getHttpMethodRestrict() != null) { - allowedMethods = "OPTIONS," + consumer.getEndpoint().getHttpMethodRestrict(); - } else { - allowedMethods = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATCH"; - } - //return list of allowed methods in response headers - httpExchange.setStatusCode(StatusCodes.OK); - httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); - httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_LENGTH, 0); - httpExchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods); - httpExchange.getResponseSender().close(); - return; - } - - //reject if the method is not allowed - if (consumer.getEndpoint().getHttpMethodRestrict() != null - && !consumer.getEndpoint().getHttpMethodRestrict().contains(requestMethod.toString())) { - httpExchange.setStatusCode(StatusCodes.METHOD_NOT_ALLOWED); - httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); - httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_LENGTH, 0); - httpExchange.getResponseSender().close(); - return; - } - - //perform blocking operation on exchange - if (httpExchange.isInIoThread()) { - httpExchange.dispatch(this); - return; - } - - //create new Exchange - //binding is used to extract header and payload(if available) - Exchange camelExchange = consumer.getEndpoint().createExchange(httpExchange); - - //Unit of Work to process the Exchange - consumer.createUoW(camelExchange); - try { - consumer.getProcessor().process(camelExchange); - } catch (Exception e) { - consumer.getExceptionHandler().handleException(e); - } finally { - consumer.doneUoW(camelExchange); - } - - Object body = getResponseBody(httpExchange, camelExchange, consumer); - TypeConverter tc = consumer.getEndpoint().getCamelContext().getTypeConverter(); - - if (body == null) { - LOG.trace("No payload to send as reply for exchange: " + camelExchange); - httpExchange.getResponseHeaders().put(ExchangeHeaders.CONTENT_TYPE, MimeMappings.DEFAULT_MIME_MAPPINGS.get("txt")); - httpExchange.getResponseSender().send("No response available"); - } else { - ByteBuffer bodyAsByteBuffer = tc.convertTo(ByteBuffer.class, body); - httpExchange.getResponseSender().send(bodyAsByteBuffer); - } - httpExchange.getResponseSender().close(); - } - - private Object getResponseBody(HttpServerExchange httpExchange, Exchange camelExchange, UndertowConsumer consumer) throws IOException { - Object result; - if (camelExchange.hasOut()) { - result = consumer.getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getOut()); - } else { - result = consumer.getEndpoint().getUndertowHttpBinding().toHttpResponse(httpExchange, camelExchange.getIn()); - } - return result; - } - - public void connectConsumer(UndertowConsumer consumer) { - consumers.put(consumer.getEndpoint().getEndpointUri(), consumer); - } -} http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowConsumerUnregisterTest.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowConsumerUnregisterTest.java b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowConsumerUnregisterTest.java index 1d6c0a2..ad06451 100644 --- a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowConsumerUnregisterTest.java +++ b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/UndertowConsumerUnregisterTest.java @@ -19,7 +19,11 @@ package org.apache.camel.component.undertow; import java.net.ConnectException; import org.apache.camel.CamelExecutionException; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.Assert; import org.junit.Test; public class UndertowConsumerUnregisterTest extends BaseUndertowTest { @@ -42,6 +46,40 @@ public class UndertowConsumerUnregisterTest extends BaseUndertowTest { } } + @Test + public void testUnregisterOneOfUndertowConsumers() throws Exception { + MockEndpoint mockFoo = getMockEndpoint("mock:foo"); + mockFoo.expectedBodiesReceived("test"); + MockEndpoint mockBar = getMockEndpoint("mock:bar"); + mockBar.expectedBodiesReceived("test", "test"); + + Processor sender = new Processor() { + public void process(Exchange exchange) { + exchange.getIn().setBody("test"); + } + }; + Exchange ret = template.request("undertow:http://localhost:{{port}}/foo", sender); + Assert.assertEquals(200, ret.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); + Assert.assertEquals("test", ret.getOut().getBody(String.class)); + ret = template.request("undertow:http://localhost:{{port}}/bar", sender); + Assert.assertEquals(200, ret.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); + Assert.assertEquals("test", ret.getOut().getBody(String.class)); + + UndertowComponent component = context.getComponent("undertow", UndertowComponent.class); + UndertowConsumer consumerFoo = (UndertowConsumer) context.getRoute("route-foo").getConsumer(); + component.unregisterConsumer(consumerFoo); + + ret = template.request("undertow:http://localhost:{{port}}/foo", sender); + Assert.assertEquals(404, ret.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); + Assert.assertEquals("No matching path found", ret.getOut().getBody(String.class)); + ret = template.request("undertow:http://localhost:{{port}}/bar", sender); + Assert.assertEquals(200, ret.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); + Assert.assertEquals("test", ret.getOut().getBody(String.class)); + + mockFoo.assertIsSatisfied(); + mockBar.assertIsSatisfied(); + } + @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { http://git-wip-us.apache.org/repos/asf/camel/blob/6e88e7a2/components/camel-undertow/src/test/resources/log4j2.properties ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/test/resources/log4j2.properties b/components/camel-undertow/src/test/resources/log4j2.properties index d969033..bfd5f67 100644 --- a/components/camel-undertow/src/test/resources/log4j2.properties +++ b/components/camel-undertow/src/test/resources/log4j2.properties @@ -26,3 +26,7 @@ appender.out.layout.type = PatternLayout appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n rootLogger.level = INFO rootLogger.appenderRef.file.ref = file + +loggers=camel-undertow +logger.camel-undertow.name=org.apache.camel.component.undertow +logger.camel-undertow.level=DEBUG