Author: davsclaus Date: Wed May 1 16:27:29 2013 New Revision: 1478078 URL: http://svn.apache.org/r1478078 Log: CAMEL-6327: More work on new camel-netty-http component.
Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultNettyHttpBinding.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientPipelineFactory.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerPipelineFactory.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpBinding.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConverter.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpMessage.java camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpClientChannelHandler.java camel/trunk/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpProducerSimpleTest.java camel/trunk/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/ClientChannelHandler.java Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultNettyHttpBinding.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultNettyHttpBinding.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultNettyHttpBinding.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/DefaultNettyHttpBinding.java Wed May 1 16:27:29 2013 @@ -58,7 +58,7 @@ public class DefaultNettyHttpBinding imp public Message toCamelMessage(HttpRequest request, Exchange exchange) throws Exception { LOG.trace("toCamelMessage: {}", request); - NettyHttpMessage answer = new NettyHttpMessage(request, this); + NettyHttpMessage answer = new NettyHttpMessage(request, null, this); // force getting headers which will populate them answer.getHeaders(); @@ -99,6 +99,46 @@ public class DefaultNettyHttpBinding imp } @Override + public Message toCamelMessage(HttpResponse response, Exchange exchange) throws Exception { + LOG.trace("toCamelMessage: {}", response); + + NettyHttpMessage answer = new NettyHttpMessage(null, response, this); + // force getting headers which will populate them + answer.getHeaders(); + + // keep the body as is, and use type converters + answer.setBody(response.getContent()); + return answer; + } + + @Override + public void populateCamelHeaders(HttpResponse response, Map<String, Object> headers, Exchange exchange) throws Exception { + LOG.trace("populateCamelHeaders: {}", response); + + headers.put(Exchange.HTTP_RESPONSE_CODE, response.getStatus().getCode()); + // TODO: use another status header + headers.put("CamelHttpResponseText", response.getStatus().getReasonPhrase()); + + for (String name : response.getHeaderNames()) { + // mapping the content-type + if (name.toLowerCase().equals("content-type")) { + name = Exchange.CONTENT_TYPE; + } + // add the headers one by one, and use the header filter strategy + List<String> values = response.getHeaders(name); + Iterator<?> it = ObjectHelper.createIterator(values); + while (it.hasNext()) { + Object extracted = it.next(); + LOG.trace("HTTP-header: {}", extracted); + if (headerFilterStrategy != null + && !headerFilterStrategy.applyFilterToExternalHeaders(name, extracted, exchange)) { + NettyHttpHelper.appendHeader(headers, name, extracted); + } + } + } + } + + @Override public HttpResponse toNettyResponse(Message message) throws Exception { LOG.trace("toNettyResponse: {}", message); Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientPipelineFactory.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientPipelineFactory.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientPipelineFactory.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientPipelineFactory.java Wed May 1 16:27:29 2013 @@ -37,14 +37,14 @@ import org.slf4j.LoggerFactory; public class HttpClientPipelineFactory extends ClientPipelineFactory { private static final Logger LOG = LoggerFactory.getLogger(HttpClientPipelineFactory.class); - private NettyProducer producer; + private NettyHttpProducer producer; private SSLContext sslContext; public HttpClientPipelineFactory() { // default constructor needed } - public HttpClientPipelineFactory(NettyProducer nettyProducer) { + public HttpClientPipelineFactory(NettyHttpProducer nettyProducer) { this.producer = nettyProducer; try { this.sslContext = createSSLContext(producer); @@ -52,12 +52,14 @@ public class HttpClientPipelineFactory e throw ObjectHelper.wrapRuntimeCamelException(e); } - LOG.info("Created SslContext {}", sslContext); + if (sslContext != null) { + LOG.info("Created SslContext {}", sslContext); + } } @Override public ClientPipelineFactory createPipelineFactory(NettyProducer nettyProducer) { - return new HttpClientPipelineFactory(nettyProducer); + return new HttpClientPipelineFactory((NettyHttpProducer) nettyProducer); } @Override Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerPipelineFactory.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerPipelineFactory.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerPipelineFactory.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerPipelineFactory.java Wed May 1 16:27:29 2013 @@ -55,7 +55,9 @@ public class HttpServerPipelineFactory e throw ObjectHelper.wrapRuntimeCamelException(e); } - LOG.info("Created SslContext {}", sslContext); + if (sslContext != null) { + LOG.info("Created SslContext {}", sslContext); + } } @Override Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpBinding.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpBinding.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpBinding.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpBinding.java Wed May 1 16:27:29 2013 @@ -50,7 +50,27 @@ public interface NettyHttpBinding { void populateCamelHeaders(HttpRequest request, Map<String, Object> headers, Exchange exchange) throws Exception; /** - * Binds from Camel {@link Message} to Netty {@link org.jboss.netty.handler.codec.http.HttpResponse}. + * Binds from Netty {@link HttpResponse} to Camel {@Message}. + * + * @param response the netty http response + * @param exchange the exchange that should contain the returned message. + * @return the message to store on the given exchange + * @throws Exception is thrown if error during binding + */ + Message toCamelMessage(HttpResponse response, Exchange exchange) throws Exception; + + /** + * Binds from Netty {@link HttpResponse} to Camel headers as a {@link Map}. + * + * @param response the netty http response + * @param headers the Camel headers that should be populated + * @param exchange the exchange that should contain the returned message. + * @throws Exception is thrown if error during binding + */ + void populateCamelHeaders(HttpResponse response, Map<String, Object> headers, Exchange exchange) throws Exception; + + /** + * Binds from Camel {@link Message} to Netty {@link HttpResponse}. * * @param message the Camel message * @return the http response Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConverter.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConverter.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConverter.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConverter.java Wed May 1 16:27:29 2013 @@ -42,7 +42,12 @@ public final class NettyHttpConverter { // okay we may need to cheat a bit when we want to grab the HttpRequest as its stored on the NettyHttpMessage // so if the message instance is a NettyHttpMessage and its body is the value, then we can grab the // HttpRequest from the NettyHttpMessage - NettyHttpMessage msg = exchange.getIn(NettyHttpMessage.class); + NettyHttpMessage msg; + if (exchange.hasOut()) { + msg = exchange.getOut(NettyHttpMessage.class); + } else { + msg = exchange.getIn(NettyHttpMessage.class); + } if (msg != null && msg.getBody() == value) { return msg.getHttpRequest(); } @@ -51,6 +56,31 @@ public final class NettyHttpConverter { return null; } + /** + * A fallback converter that allows us to easily call Java beans and use the raw Netty {@link HttpRequest} as parameter types. + */ + @FallbackConverter + public static Object convertToHttpResponse(Class<?> type, Exchange exchange, Object value, TypeConverterRegistry registry) { + // if we want to covert to convertToHttpResponse + if (value != null && HttpResponse.class.isAssignableFrom(type)) { + + // okay we may need to cheat a bit when we want to grab the HttpRequest as its stored on the NettyHttpMessage + // so if the message instance is a NettyHttpMessage and its body is the value, then we can grab the + // HttpRequest from the NettyHttpMessage + NettyHttpMessage msg; + if (exchange.hasOut()) { + msg = exchange.getOut(NettyHttpMessage.class); + } else { + msg = exchange.getIn(NettyHttpMessage.class); + } + if (msg != null && msg.getBody() == value) { + return msg.getHttpResponse(); + } + } + + return null; + } + @Converter public static String toString(HttpResponse response, Exchange exchange) { String contentType = response.getHeader(Exchange.CONTENT_TYPE); Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpMessage.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpMessage.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpMessage.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpMessage.java Wed May 1 16:27:29 2013 @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.camel.RuntimeCamelException; import org.apache.camel.impl.DefaultMessage; import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponse; /** * Netty HTTP based {@link org.apache.camel.Message}. @@ -31,10 +32,12 @@ import org.jboss.netty.handler.codec.htt public class NettyHttpMessage extends DefaultMessage { private final transient HttpRequest httpRequest; + private final transient HttpResponse httpResponse; private final transient NettyHttpBinding httpBinding; - public NettyHttpMessage(HttpRequest httpRequest, NettyHttpBinding httpBinding) { + public NettyHttpMessage(HttpRequest httpRequest, HttpResponse httpResponse, NettyHttpBinding httpBinding) { this.httpRequest = httpRequest; + this.httpResponse = httpResponse; this.httpBinding = httpBinding; } @@ -42,10 +45,18 @@ public class NettyHttpMessage extends De return httpRequest; } + public HttpResponse getHttpResponse() { + return httpResponse; + } + @Override protected void populateInitialHeaders(Map<String, Object> map) { try { - httpBinding.populateCamelHeaders(httpRequest, map, getExchange()); + if (httpRequest != null) { + httpBinding.populateCamelHeaders(httpRequest, map, getExchange()); + } else { + httpBinding.populateCamelHeaders(httpResponse, map, getExchange()); + } } catch (Exception e) { throw new RuntimeCamelException("Error populating initial headers", e); } @@ -53,6 +64,6 @@ public class NettyHttpMessage extends De @Override public DefaultMessage newInstance() { - return new NettyHttpMessage(httpRequest, httpBinding); + return new NettyHttpMessage(httpRequest, httpResponse, httpBinding); } } Modified: camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpClientChannelHandler.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpClientChannelHandler.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpClientChannelHandler.java (original) +++ camel/trunk/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpClientChannelHandler.java Wed May 1 16:27:29 2013 @@ -16,26 +16,44 @@ */ package org.apache.camel.component.netty.http.handlers; -import org.apache.camel.component.netty.NettyProducer; +import org.apache.camel.Exchange; +import org.apache.camel.Message; import org.apache.camel.component.netty.handlers.ClientChannelHandler; import org.apache.camel.component.netty.http.NettyHttpProducer; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.codec.http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Netty HTTP {@link org.apache.camel.component.netty.handlers.ClientChannelHandler} that handles the response combing + * back from thhe HTTP server, called by this client. + * + */ public class HttpClientChannelHandler extends ClientChannelHandler { - // use NettyHttpConsumer as logger to make it easier to read the logs as this is part of the producer + // use NettyHttpProducer as logger to make it easier to read the logs as this is part of the producer private static final transient Logger LOG = LoggerFactory.getLogger(NettyHttpProducer.class); + private final NettyHttpProducer producer; + private HttpResponse response; - public HttpClientChannelHandler(NettyProducer producer) { + public HttpClientChannelHandler(NettyHttpProducer producer) { super(producer); + this.producer = producer; } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception { - super.messageReceived(ctx, messageEvent); //To change body of overridden methods use File | Settings | File Templates. + // store response, as this channel handler is created per pipeline + response = (HttpResponse) messageEvent.getMessage(); + + super.messageReceived(ctx, messageEvent); } + @Override + protected Message getResponseMessage(Exchange exchange, MessageEvent messageEvent) throws Exception { + // use the binding + return producer.getEndpoint().getNettyHttpBinding().toCamelMessage(response, exchange); + } } Modified: camel/trunk/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpProducerSimpleTest.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpProducerSimpleTest.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpProducerSimpleTest.java (original) +++ camel/trunk/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpProducerSimpleTest.java Wed May 1 16:27:29 2013 @@ -16,7 +16,10 @@ */ package org.apache.camel.component.netty.http; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; import org.apache.camel.builder.RouteBuilder; +import org.jboss.netty.handler.codec.http.HttpResponse; import org.junit.Test; public class NettyHttpProducerSimpleTest extends BaseNettyTest { @@ -31,6 +34,31 @@ public class NettyHttpProducerSimpleTest assertMockEndpointsSatisfied(); } + @Test + public void testHttpSimpleExchange() throws Exception { + getMockEndpoint("mock:input").expectedBodiesReceived("Hello World"); + + Exchange out = template.request("netty-http:http://localhost:{{port}}/foo", new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getIn().setBody("Hello World"); + } + }); + assertNotNull(out); + assertTrue(out.hasOut()); + + NettyHttpMessage response = out.getOut(NettyHttpMessage.class); + assertNotNull(response); + assertEquals(200, response.getHttpResponse().getStatus().getCode()); + + // we can also get the response as body + HttpResponse body = out.getOut().getBody(HttpResponse.class); + assertNotNull(body); + assertEquals(200, body.getStatus().getCode()); + + assertMockEndpointsSatisfied(); + } + @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { Modified: camel/trunk/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/ClientChannelHandler.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/ClientChannelHandler.java?rev=1478078&r1=1478077&r2=1478078&view=diff ============================================================================== --- camel/trunk/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/ClientChannelHandler.java (original) +++ camel/trunk/components/camel-netty/src/main/java/org/apache/camel/component/netty/handlers/ClientChannelHandler.java Wed May 1 16:27:29 2013 @@ -19,7 +19,7 @@ package org.apache.camel.component.netty import org.apache.camel.AsyncCallback; import org.apache.camel.CamelExchangeException; import org.apache.camel.Exchange; -import org.apache.camel.NoTypeConversionAvailableException; +import org.apache.camel.Message; import org.apache.camel.component.netty.NettyCamelState; import org.apache.camel.component.netty.NettyConstants; import org.apache.camel.component.netty.NettyHelper; @@ -125,27 +125,20 @@ public class ClientChannelHandler extend Exchange exchange = getExchange(ctx); AsyncCallback callback = getAsyncCallback(ctx); - Object body = messageEvent.getMessage(); - if (LOG.isDebugEnabled()) { - LOG.debug("Channel: {} received body: {}", new Object[]{messageEvent.getChannel(), body}); - } - - // if textline enabled then covert to a String which must be used for textline - if (producer.getConfiguration().isTextline()) { - try { - body = producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body); - } catch (NoTypeConversionAvailableException e) { - exchange.setException(e); - callback.done(false); - } + Message message; + try { + message = getResponseMessage(exchange, messageEvent); + } catch (Exception e) { + exchange.setException(e); + callback.done(false); + return; } - // set the result on either IN or OUT on the original exchange depending on its pattern if (ExchangeHelper.isOutCapable(exchange)) { - NettyPayloadHelper.setOut(exchange, body); + exchange.setOut(message); } else { - NettyPayloadHelper.setIn(exchange, body); + exchange.setIn(message); } try { @@ -174,6 +167,37 @@ public class ClientChannelHandler extend } } + /** + * Gets the Camel {@link Message} to use as the message to be set on the current {@link Exchange} when + * we have received a reply message. + * <p/> + * + * @param exchange the current exchange + * @param messageEvent the incoming event which has the response message from Netty. + * @return the Camel {@link Message} to set on the current {@link Exchange} as the response message. + * @throws Exception is thrown if error getting the response message + */ + protected Message getResponseMessage(Exchange exchange, MessageEvent messageEvent) throws Exception { + Object body = messageEvent.getMessage(); + if (LOG.isDebugEnabled()) { + LOG.debug("Channel: {} received body: {}", new Object[]{messageEvent.getChannel(), body}); + } + + // if textline enabled then covert to a String which must be used for textline + if (producer.getConfiguration().isTextline()) { + body = producer.getContext().getTypeConverter().mandatoryConvertTo(String.class, exchange, body); + } + + // set the result on either IN or OUT on the original exchange depending on its pattern + if (ExchangeHelper.isOutCapable(exchange)) { + NettyPayloadHelper.setOut(exchange, body); + return exchange.getOut(); + } else { + NettyPayloadHelper.setIn(exchange, body); + return exchange.getIn(); + } + } + private Exchange getExchange(ChannelHandlerContext ctx) { NettyCamelState state = producer.getState(ctx.getChannel()); return state != null ? state.getExchange() : null;