This is an automated email from the ASF dual-hosted git repository. orpiske pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit b7f21524fc855743fd61360a92df92c32fafef25 Author: Otavio Rodolfo Piske <[email protected]> AuthorDate: Wed Feb 21 14:32:33 2024 +0100 CAMEL-20410: documentation fixes for camel-netty - Fixed samples - Fixed grammar and typos - Fixed punctuation - Added and/or fixed links - Converted to use tabs --- .../camel-netty/src/main/docs/netty-component.adoc | 467 +++++++++++---------- 1 file changed, 240 insertions(+), 227 deletions(-) diff --git a/components/camel-netty/src/main/docs/netty-component.adoc b/components/camel-netty/src/main/docs/netty-component.adoc index 15686781f71..21b429e70cb 100644 --- a/components/camel-netty/src/main/docs/netty-component.adoc +++ b/components/camel-netty/src/main/docs/netty-component.adoc @@ -15,18 +15,18 @@ *{component-header}* The Netty component in Camel is a socket communication component, -based on the http://netty.io/[Netty] project version 4. + - Netty is a NIO client server framework which enables quick and easy +based on the http://netty.io/[Netty] project version 4. +Netty is a NIO client server framework that enables quick and easy development of networkServerInitializerFactory applications such as -protocol servers and clients. + - Netty greatly simplifies and streamlines network programming such as +protocol servers and clients. +Netty greatly simplifies and streamlines network programming such as TCP and UDP socket server. -This camel component supports both producer and consumer endpoints. +This Camel component supports both producer and consumer endpoints. The Netty component has several options and allows fine-grained control of a number of TCP/UDP communication parameters (buffer sizes, -keepAlives, tcpNoDelay, etc) and facilitates both In-Only and In-Out +`keepAlive`, `tcpNoDelay`, etc.), and facilitates both In-Only and In-Out communication on a Camel route. Maven users will need to add the following dependency to their `pom.xml` @@ -46,11 +46,17 @@ for this component: The URI scheme for a netty component is as follows +.TCP ---- netty:tcp://0.0.0.0:99999[?options] +---- + +.UDP +---- netty:udp://remotehost:99999/[?options] ---- + This component supports producer and consumer endpoints for both TCP and UDP. @@ -71,35 +77,35 @@ include::partial$component-endpoint-headers.adoc[] // component headers: END -== Registry based Options +== Registry-based Options Codec Handlers and SSL Keystores can be enlisted in the Registry, such as in the Spring XML file. -The values that could be passed in, are the following: +The values that could be passed in are the following: [width="100%",cols="10%,90%",options="header",] |=== |Name |Description -|`passphrase` |password setting to use in order to encrypt/decrypt payloads sent using +|`passphrase` |password setting to use to encrypt/decrypt payloads sent using SSH -|`keyStoreFormat` |keystore format to be used for payload encryption. Defaults to "JKS" if +|`keyStoreFormat` |keystore format to be used for payload encryption. Defaults to `JKS` if not set |`securityProvider` |Security provider to be used for payload encryption. Defaults to -"SunX509" if not set. +`SunX509` if not set. |`keyStoreFile` |*deprecated:* Client side certificate keystore to be used for encryption |`trustStoreFile` |*deprecated:* Server side certificate keystore to be used for encryption |`keyStoreResource` |Client side certificate keystore to be used for -encryption. Is loaded by default from classpath, but you can prefix with +encryption. It is loaded by default from classpath, but you can prefix with `"classpath:"`, `"file:"`, or `"http:"` to load the resource from different systems. |`trustStoreResource` |Server side certificate keystore to be used for -encryption. Is loaded by default from classpath, but you can prefix with +encryption. It is loaded by default from classpath, but you can prefix with `"classpath:"`, `"file:"`, or `"http:"` to load the resource from different systems. @@ -107,31 +113,31 @@ different systems. |`encoder` |A custom `ChannelHandler` class that can be used to perform special marshalling of outbound payloads. Must override -io.netty.channel.ChannelInboundHandlerAdapter. +`io.netty.channel.ChannelInboundHandlerAdapter`. -|`encoders` |A list of encoders to be used. You can use a String which have values +|`encoders` |A list of encoders to be used. You can use a string that has values separated by comma, and have the values be looked up in the -Registry. Just remember to prefix the value with # -so Camel knows it should lookup. +Registry. Remember to prefix the value with `#` so Camel knows it should look up. |`decoder` |A custom `ChannelHandler` class that can be used to perform special marshalling of inbound payloads. Must override -io.netty.channel.ChannelOutboundHandlerAdapter. +`io.netty.channel.ChannelOutboundHandlerAdapter`. -|`decoders` |A list of decoders to be used. You can use a String which have values +|`decoders` |A list of decoders to be used. You can use a string that has values separated by comma, and have the values be looked up in the -Registry. Just remember to prefix the value with # -so Camel knows it should lookup. +Registry. Remember to prefix the value with `#` +so Camel knows it should look up. |=== [NOTE] ==== -Read below about using non shareable encoders/decoders. +Read below about using non-shareable encoders/decoders. ==== -=== Using non shareable encoders or decoders +[[Netty-NonShareableEncodersOrDecoders]] +=== Using non-shareable encoders or decoders -If your encoders or decoders are not shareable (e.g. they don't have the +If your encoders or decoders are not shareable (e.g., they don't have the @Shareable class annotation), then your encoder/decoder must implement the `org.apache.camel.component.netty.ChannelHandlerFactory` interface, and return a new instance in the `newChannelHandler` method. This is to @@ -155,188 +161,34 @@ The producer mode supports both one-way and request-response based operations. In Consumer mode, the component provides the ability to: -* listen on a specified socket using either TCP or UDP protocols (with +* listen to a specified socket using either TCP or UDP protocols (with optional SSL support), * receive requests on the socket using text/xml, binary and serialized -object based payloads and +object-based payloads and * send them along on a route as message exchanges. The consumer mode supports both one-way and request-response based operations. -== Examples - -=== A UDP Netty endpoint using Request-Reply and serialized object payload - -Note that Object serialization is not allowed by default, and so a decoder must be configured. - -[source,java] ----- -@BindToRegistry("decoder") -public ChannelHandler getDecoder() throws Exception { - return new DefaultChannelHandlerFactory() { - @Override - public ChannelHandler newChannelHandler() { - return new DatagramPacketObjectDecoder(ClassResolvers.weakCachingResolver(null)); - } - }; -} - -RouteBuilder builder = new RouteBuilder() { - public void configure() { - from("netty:udp://0.0.0.0:5155?sync=true&decoders=#decoder") - .process(new Processor() { - public void process(Exchange exchange) throws Exception { - Poetry poetry = (Poetry) exchange.getIn().getBody(); - // Process poetry in some way - exchange.getOut().setBody("Message received); - } - } - } -}; ----- - -=== A TCP based Netty consumer endpoint using One-way communication - -[source,java] ----- -RouteBuilder builder = new RouteBuilder() { - public void configure() { - from("netty:tcp://0.0.0.0:5150") - .to("mock:result"); - } -}; ----- - -=== An SSL/TCP based Netty consumer endpoint using Request-Reply communication - -[[Netty-UsingtheJSSEConfigurationUtility]] -Using the JSSE Configuration Utility - -The Netty component supports SSL/TLS configuration -through the xref:manual::camel-configuration-utilities.adoc[Camel JSSE -Configuration Utility]. This utility greatly decreases the amount of -component specific code you need to write and is configurable at the -endpoint and component levels. The following examples demonstrate how -to use the utility with the Netty component. - -[[Netty-Programmaticconfigurationofthecomponent]] -Programmatic configuration of the component - -[source,java] ----- -KeyStoreParameters ksp = new KeyStoreParameters(); -ksp.setResource("/users/home/server/keystore.jks"); -ksp.setPassword("keystorePassword"); - -KeyManagersParameters kmp = new KeyManagersParameters(); -kmp.setKeyStore(ksp); -kmp.setKeyPassword("keyPassword"); - -SSLContextParameters scp = new SSLContextParameters(); -scp.setKeyManagers(kmp); - -NettyComponent nettyComponent = getContext().getComponent("netty", NettyComponent.class); -nettyComponent.getConfiguration().setSslContextParameters(scp); ----- - -[[Netty-SpringDSLbasedconfigurationofendpoint]] -Spring DSL based configuration of endpoint - -[source,xml] ----- -... - <camel:sslContextParameters - id="sslContextParameters"> - <camel:keyManagers - keyPassword="keyPassword"> - <camel:keyStore - resource="/users/home/server/keystore.jks" - password="keystorePassword"/> - </camel:keyManagers> - </camel:sslContextParameters>... -... - <to uri="netty:tcp://0.0.0.0:5150?sync=true&ssl=true&sslContextParameters=#sslContextParameters"/> -... ----- - -[[Netty-UsingBasicSSLTLSconfigurationontheJettyComponent]] -Using Basic SSL/TLS configuration on the Jetty Component - -[source,java] ----- -Registry registry = context.getRegistry(); -registry.bind("password", "changeit"); -registry.bind("ksf", new File("src/test/resources/keystore.jks")); -registry.bind("tsf", new File("src/test/resources/keystore.jks")); - -context.addRoutes(new RouteBuilder() { - public void configure() { - String netty_ssl_endpoint = - "netty:tcp://0.0.0.0:5150?sync=true&ssl=true&passphrase=#password" - + "&keyStoreFile=#ksf&trustStoreFile=#tsf"; - String return_string = - "When You Go Home, Tell Them Of Us And Say," - + "For Your Tomorrow, We Gave Our Today."; - - from(netty_ssl_endpoint) - .process(new Processor() { - public void process(Exchange exchange) throws Exception { - exchange.getOut().setBody(return_string); - } - } - } -}); ----- - -[[Netty-GettingaccesstoSSLSessionandtheclientcertificate]] -Getting access to SSLSession and the client certificate - -You can get access to the `javax.net.ssl.SSLSession` if you eg need to -get details about the client certificate. When `ssl=true` then the -xref:netty-component.adoc[Netty] component will store the `SSLSession` as a -header on the Camel Message as shown below: - -[source,java] ----- -SSLSession session = exchange.getIn().getHeader(NettyConstants.NETTY_SSL_SESSION, SSLSession.class); -// get the first certificate which is client certificate -javax.security.cert.X509Certificate cert = session.getPeerCertificateChain()[0]; -Principal principal = cert.getSubjectDN(); ----- - -Remember to set `needClientAuth=true` to authenticate the client, -otherwise `SSLSession` cannot access information about the client -certificate, and you may get an exception -`javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated`. You -may also get this exception if the client certificate is expired or not -valid etc. - -[TIP] -==== -The option `sslClientCertHeaders` can be set to `true` which then -enriches the Camel Message with headers having -details about the client certificate. For example the subject name is -readily available in the header `CamelNettySSLClientCertSubjectName`. -==== - === Using Multiple Codecs -In certain cases it may be necessary to add chains of encoders and -decoders to the netty pipeline. To add multpile codecs to a camel netty -endpoint the 'encoders' and 'decoders' uri parameters should be used. -Like the 'encoder' and 'decoder' parameters they are used to supply -references (lists of ChannelUpstreamHandlers and -ChannelDownstreamHandlers) that should be added to the pipeline. Note -that if encoders is specified then the encoder param will be ignored, +In certain cases, it may be necessary to add chains of encoders and +decoders to the netty pipeline. +To add multiple codecs to a Camel netty endpoint, the `encoders` and +`decoders` uri parameters should be used. +Like the `encoder` and `decoder` parameters they are used to supply +references (lists of `ChannelUpstreamHandlers` and +`ChannelDownstreamHandlers`) that should be added to the pipeline. + +Note that if encoders are specified, then the encoder param will be ignored, similarly for decoders and the decoder param. [NOTE] ==== -Read further above about using non shareable encoders/decoders. +Read further about using <<Netty-NonShareableEncodersOrDecoders,non-shareable encoders/decoders>>. ==== -The lists of codecs need to be added to the Camel's registry so they can +The lists of codecs need to be added to the Camel's registry, so they can be resolved when the endpoint is created. [source,java] @@ -403,8 +255,13 @@ lists in an application context ---- The bean names can then be used in netty endpoint definitions either as -a comma separated list or contained in a List e.g. +a comma-separated list or contained in a list, e.g.: + +[tabs] +==== +Java:: ++ [source,java] ---- from("direct:multiple-codec").to("netty:tcp://0.0.0.0:{{port}}?encoders=#encoders&sync=false"); @@ -412,8 +269,8 @@ a comma separated list or contained in a List e.g. from("netty:tcp://0.0.0.0:{{port}}?decoders=#length-decoder,#string-decoder&sync=false").to("mock:multiple-codec"); ---- -or via XML. - +XML:: ++ [source,xml] ---- <camelContext id="multiple-netty-codecs-context" xmlns="http://camel.apache.org/schema/spring"> @@ -428,18 +285,19 @@ or via XML. </camelContext> ---- +==== + == Closing Channel When Complete -When acting as a server you sometimes want to close the channel when, -for example, a client conversion is finished. + - You can do this by simply setting the endpoint option -`disconnect=true`. +When acting as a server, you sometimes want to close the channel when, +for example, a client conversion is finished. +You can do this by simply setting the endpoint option `disconnect=true`. -However you can also instruct Camel on a per message basis as follows. + - To instruct Camel to close the channel, you should add a header with +However, you can also instruct Camel on a per-message basis as follows. +To instruct Camel to close the channel, you should add a header with the key `CamelNettyCloseChannelWhenComplete` set to a boolean `true` -value. + - For instance, the example below will close the channel after it has +value. +For instance, the example below will close the channel after it has written the bye message back to the client: [source,java] @@ -448,7 +306,7 @@ from("netty:tcp://0.0.0.0:8080").process(new Processor() { public void process(Exchange exchange) throws Exception { String body = exchange.getIn().getBody(String.class); exchange.getOut().setBody("Bye " + body); - // some condition which determines if we should close + // some condition that determines if we should close if (close) { exchange.getOut().setHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, true); } @@ -464,24 +322,24 @@ Adding custom channel pipeline factories to gain complete control over a created Custom channel pipelines provide complete control to the user over the handler/interceptor chain by inserting custom handler(s), encoder(s) & decoder(s) without having to specify them in the Netty Endpoint URL in a -very simple way. +straightforward way. -In order to add a custom pipeline, a custom channel pipeline factory +To add a custom pipeline, a custom channel pipeline factory must be created and registered with the context via the context registry -(Registry, or the camel-spring ApplicationContextRegistry etc). +(or the camel-spring `ApplicationContextRegistry`, etc). A custom pipeline factory must be constructed as follows -* A Producer linked channel pipeline factory must extend the abstract +* A Producer-linked channel pipeline factory must extend the abstract class `ClientInitializerFactory`. -* A Consumer linked channel pipeline factory must extend the abstract +* A Consumer-linked channel pipeline factory must extend the abstract class `ServerInitializerFactory`. -* The classes should override the initChannel() method in order to +* The classes should override the `initChannel()` method to insert custom handler(s), encoder(s) and decoder(s). Not overriding the `initChannel()` method creates a pipeline with no handlers, encoders or decoders wired to the pipeline. -The example below shows how ServerInitializerFactory factory may be +The example below shows how `ServerInitializerFactory` factory may be created === Using custom pipeline factory @@ -497,14 +355,14 @@ public class SampleServerInitializerFactory extends ServerInitializerFactory { channelPipeline.addLast("encoder-SD", new StringEncoder(CharsetUtil.UTF_8)); channelPipeline.addLast("decoder-DELIM", new DelimiterBasedFrameDecoder(maxLineSize, true, Delimiters.lineDelimiter())); channelPipeline.addLast("decoder-SD", new StringDecoder(CharsetUtil.UTF_8)); - // here we add the default Camel ServerChannelHandler for the consumer, to allow Camel to route the message etc. + // here we add the default Camel ServerChannelHandler for the consumer, to allow Camel to route the message, etc. channelPipeline.addLast("handler", new ServerChannelHandler(consumer)); } } ---- The custom channel pipeline factory can then be added to the registry -and instantiated/utilized on a camel route in the following way +and instantiated/utilized on a Camel route in the following way [source,java] ---- @@ -531,14 +389,14 @@ context.addRoutes(new RouteBuilder() { == Reusing Netty boss and worker thread pools -Netty has two kind of thread pools: boss and worker. By default each +Netty has two kinds of thread pools: boss and worker. By default, each Netty consumer and producer has their private thread pools. If you want -to reuse these thread pools among multiple consumers or producers then +to reuse these thread pools among multiple consumers or producers, then the thread pools must be created and enlisted in the Registry. -For example using Spring XML we can create a shared worker thread pool -using the `NettyWorkerPoolBuilder` with 2 worker threads as shown below: +For example, using Spring XML we can create a shared worker thread pool +using the `NettyWorkerPoolBuilder` with two worker threads as shown below: [source,xml] ---- @@ -574,7 +432,7 @@ configuring the `workerPool` option in the URI as shown below: </route> ---- -And if we have another route we can refer to the shared worker pool: +And if we have another route, we can refer to the shared worker pool: [source,xml] ---- @@ -585,24 +443,24 @@ And if we have another route we can refer to the shared worker pool: </route> ---- -and so forth. +And so forth. == Multiplexing concurrent messages over a single connection with request/reply -When using Netty for request/reply messaging via the netty producer then by default each +When using Netty for request/reply messaging via the netty producer, then by default, each message is sent via a non-shared connection (pooled). This ensures that replies are automatic being able to map to the correct request thread for further routing in Camel. -In other words correlation between request/reply messages happens out-of-the-box because -the replies comes back on the same connection that was used for sending the request; +In other words, correlation between request/reply messages happens out-of-the-box because +the replies come back on the same connection that was used for sending the request; and this connection is not shared with others. When the response comes back, the connection -is returned back to the connection pool, where it can be reused by others. +is returned to the connection pool, where it can be reused by others. -However if you want to multiplex concurrent request/responses on a single shared connection, +However, if you want to multiplex concurrent request/responses on a single shared connection, then you need to turn off the connection pooling by setting `producerPoolEnabled=false`. -Now this means there is a potential issue with interleaved responses if replies comes back out-of-order. -Therefore you need to have a correlation id in both the request and reply messages so you can properly +Now this means there is a potential issue with interleaved responses if replies come back out-of-order. +Therefore, you need to have a correlation id in both the request and reply messages, so you can properly correlate the replies to the Camel callback that is responsible for continue processing the message in Camel. -To do this you need to implement `NettyCamelStateCorrelationManager` as correlation manager and configure +To do this, you need to implement `NettyCamelStateCorrelationManager` as correlation manager and configure it via the `correlationManager=#myManager` option. [NOTE] @@ -616,7 +474,8 @@ under the `camel-example-netty-custom-correlation` directory. == Native transport -To enable native transport you need to add additional dependency for epoll or kqueue depending on your OS and CPU arch. To make it easier add the following extension to your `build` section of `pom.xml`: +To enable native transport, you need to add additional dependency for epoll or kqueue depending on your OS and CPU arch. +To make it easier add the following extension to your `build` section of `pom.xml`: [source,xml] ---- @@ -655,5 +514,159 @@ MacOS/BSD:: ---- ==== +== Examples + +=== A UDP Netty endpoint using Request-Reply and serialized object payload + +Note that Object serialization is not allowed by default, and so a decoder must be configured. + +[source,java] +---- +@BindToRegistry("decoder") +public ChannelHandler getDecoder() throws Exception { + return new DefaultChannelHandlerFactory() { + @Override + public ChannelHandler newChannelHandler() { + return new DatagramPacketObjectDecoder(ClassResolvers.weakCachingResolver(null)); + } + }; +} + +RouteBuilder builder = new RouteBuilder() { + public void configure() { + from("netty:udp://0.0.0.0:5155?sync=true&decoders=#decoder") + .process(new Processor() { + public void process(Exchange exchange) throws Exception { + Poetry poetry = (Poetry) exchange.getIn().getBody(); + // Process poetry in some way + exchange.getOut().setBody("Message received); + } + } + } +}; +---- + +=== A TCP-based Netty consumer endpoint using One-way communication + +[source,java] +---- +RouteBuilder builder = new RouteBuilder() { + public void configure() { + from("netty:tcp://0.0.0.0:5150") + .to("mock:result"); + } +}; +---- + +=== An SSL/TCP-based Netty consumer endpoint using Request-Reply communication + +[[Netty-UsingtheJSSEConfigurationUtility]] +Using the JSSE Configuration Utility + +The Netty component supports SSL/TLS configuration +through the xref:manual::camel-configuration-utilities.adoc[Camel JSSE Configuration Utility]. +This utility greatly decreases the amount of component-specific code +you need to write and is configurable at the endpoint and component levels. +The following examples demonstrate how to use the utility with the Netty component. + +[[Netty-Programmaticconfigurationofthecomponent]] +Programmatic configuration of the component + +[source,java] +---- +KeyStoreParameters ksp = new KeyStoreParameters(); +ksp.setResource("/users/home/server/keystore.jks"); +ksp.setPassword("keystorePassword"); + +KeyManagersParameters kmp = new KeyManagersParameters(); +kmp.setKeyStore(ksp); +kmp.setKeyPassword("keyPassword"); + +SSLContextParameters scp = new SSLContextParameters(); +scp.setKeyManagers(kmp); + +NettyComponent nettyComponent = getContext().getComponent("netty", NettyComponent.class); +nettyComponent.getConfiguration().setSslContextParameters(scp); +---- + +[[Netty-SpringDSLbasedconfigurationofendpoint]] +Spring DSL based configuration of endpoint + +[source,xml] +---- +... + <camel:sslContextParameters + id="sslContextParameters"> + <camel:keyManagers + keyPassword="keyPassword"> + <camel:keyStore + resource="/users/home/server/keystore.jks" + password="keystorePassword"/> + </camel:keyManagers> + </camel:sslContextParameters>... +... + <to uri="netty:tcp://0.0.0.0:5150?sync=true&ssl=true&sslContextParameters=#sslContextParameters"/> +... +---- + +[[Netty-UsingBasicSSLTLSconfigurationontheJettyComponent]] +Using Basic SSL/TLS configuration on the Jetty Component + +[source,java] +---- +Registry registry = context.getRegistry(); +registry.bind("password", "changeit"); +registry.bind("ksf", new File("src/test/resources/keystore.jks")); +registry.bind("tsf", new File("src/test/resources/keystore.jks")); + +context.addRoutes(new RouteBuilder() { + public void configure() { + String netty_ssl_endpoint = + "netty:tcp://0.0.0.0:5150?sync=true&ssl=true&passphrase=#password" + + "&keyStoreFile=#ksf&trustStoreFile=#tsf"; + String return_string = + "When You Go Home, Tell Them Of Us And Say," + + "For Your Tomorrow, We Gave Our Today."; + + from(netty_ssl_endpoint) + .process(new Processor() { + public void process(Exchange exchange) throws Exception { + exchange.getOut().setBody(return_string); + } + } + } +}); +---- + +[[Netty-GettingaccesstoSSLSessionandtheclientcertificate]] +Getting access to SSLSession and the client certificate + +You can get access to the `javax.net.ssl.SSLSession` if you e.g., need to +get details about the client certificate. When `ssl=true` then the +xref:netty-component.adoc[Netty] component will store the `SSLSession` as a +header on the Camel Message as shown below: + +[source,java] +---- +SSLSession session = exchange.getIn().getHeader(NettyConstants.NETTY_SSL_SESSION, SSLSession.class); +// get the first certificate which is client certificate +javax.security.cert.X509Certificate cert = session.getPeerCertificateChain()[0]; +Principal principal = cert.getSubjectDN(); +---- + +Remember to set `needClientAuth=true` to authenticate the client, +otherwise `SSLSession` cannot access information about the client +certificate, and you may get an exception +`javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated`. +You may also get this exception if the client certificate is expired +or not valid, etc. + +[TIP] +==== +The option `sslClientCertHeaders` can be set to `true` which then +enriches the Camel Message with headers having +details about the client certificate. For example, the subject name is +readily available in the header `CamelNettySSLClientCertSubjectName`. +==== include::spring-boot:partial$starter.adoc[]
