6 Replies Latest reply on Jan 6, 2014 11:42 AM by darren hartford

    Example for adding TCP (Netty) encoder/decoders (registry?)

    darren hartford Expert

      Hi all,

      Using Switchyard 1.1, it is unclear how to add custom encoders/decoders and have it usable with the Netty binding in Switchyard.

       

      Creating a new Switchyard project (updating to 1.1.0.Final), I've added the beans defined here into the Switchyard precreated META-INF/beans.xml file:

       

      http://camel.apache.org/netty.html  (Spring's native collections support can be used to specify the codec lists in an application context section)

      (adding xmlns:util="http://www.springframework.org/schema/util" for the util list in beans.xml)

       

      then I add the reference to the switchyard xml binding (note sync=true for request/reply):

       

      <sca:service name="route" promote="Route/route">

            <sy:interface.esb inputType="java:java.lang.String"/>

            <netty:binding.tcp name="tcp6000">

              <netty:host>0.0.0.0</netty:host>

              <netty:port>6000</netty:port>

              <netty:allowDefaultCodec>false</netty:allowDefaultCodec>

              <netty:sync>true</netty:sync>

              <netty:decoders>#length-decoder</netty:decoders>

            </netty:binding.tcp>

          </sca:service>

       

      But I get the following errors (abbreviated):

       

       

      Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: netty://tcp://0.0.0.0:6000?allow

      DefaultCodec=false&decoders=%23length-decoder&sync=true due to: No bean could be found in the registry for: length-deco

      der of type: java.lang.Object

              at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:469)

              at org.apache.camel.util.CamelContextHelper.getMandatoryEndpoint(CamelContextHelper.java:50)

              at org.apache.camel.model.RouteDefinition.resolveEndpoint(RouteDefinition.java:186)

              at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:107)

              at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:113)

              at org.apache.camel.model.FromDefinition.resolveEndpoint(FromDefinition.java:72)

              at org.apache.camel.impl.DefaultRouteContext.getEndpoint(DefaultRouteContext.java:89)

              at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:851)

              at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:171)

              ... 17 more

      Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: length-decoder of type: jav

      a.lang.Object

       

       

      Note: The end goal is to evaluate Switchyard using a request/reply exchange usecase from a TCP payload to an HTTP reference and back, so if someone already has an example or known gotcha's for this usecase, please share :-)

       

      Thanks in advance for any feedback!

       

      -D

        • 1. Re: Example for adding TCP (Netty) encoder/decoders (registry?)
          darren hartford Expert

          After going through forum, blog, google, I dug through some old IRC logs and after some trial and error here are my assumptions:

           

          1. you can not use existing spring dsl (xml) configurations with switchyard when working with camel components that contain references (#bean) in the configuration like netty (TCP/UDP).  (i.e. you can not add your normal bean id/bean ref to beans.xml nor switchyard.xml).
          2. instead, you must use CDI, requiring java code.  (can not do it simply through configuration, even if there happens to already be an appropriate codec already in the classpath...but a custom codec would always require classpath/coding anyway).
          3. The CDI registry in Switchyard seems to be equiv of the camel registry.

           

          To use CDI for the purposes of encoder/decoders, create a class (name not relevant, but I used 'NettyCodecs'), create methods that must return ChannelHandlerFactory.


          the examples below simulate near the bottom of http://camel.apache.org/netty.html  (Spring's native collections support can be used to specify the codec lists in an application context section)

           

          @Produces @Named  ("myidentifier")  tags applied to those methods.

           

           

           

          Header 1
          @Produces
          @Named ("lengthDecoder")
          ChannelHandlerFactory lengthDecoder() {
          /* replaces:

          <bean id="length-decoder" class="org.apache.camel.component.netty.ChannelHandlerFactories" factory-method="newLengthFieldBasedFrameDecoder">

          <constructor-arg value="1048576"/>

          <constructor-arg value="0"/>

          <constructor-arg value="4"/>

          <constructor-arg value="0"/>

          <constructor-arg value="4"/>

          </bean>*/
          ChannelHandlerFactory newLengthFieldBasedFrameDecoder = org.apache.camel.component.netty.ChannelHandlerFactories.newLengthFieldBasedFrameDecoder(200, 0, 4, 0, 4);
          return newLengthFieldBasedFrameDecoder;
          }
          @Produces
          @Named ("stringEncoder")
          ChannelHandlerFactory stringEncoder() {
          /* replaces:

              <bean id="string-encoder" class="org.jboss.netty.handler.codec.string.StringEncoder"/>

          */
          return org.apache.camel.component.netty.ChannelHandlerFactories.newStringEncoder(Charset.defaultCharset());
          }

           

           

           

          Then in your switchyard.xml binding, reference the @named value with a # in front of it

           

          Header 1

          <sca:service name="route" promote="Route/route">

                <sy:interface.esb inputType="java:java.lang.String"/>

                <netty:binding.tcp name="tcp6000">

                  <netty:host>0.0.0.0</netty:host>

                  <netty:port>6000</netty:port>

                  <netty:encoders>#stringEncoder</netty:encoders>

                  <netty:allowDefaultCodec>false</netty:allowDefaultCodec>

                  <netty:sync>true</netty:sync>

                </netty:binding.tcp>

              </sca:service>

           

           

          hope this helps someone else, and is the 'right/best' way to handle the scenario :-)

           

          -D

          • 2. Re: Re: Example for adding TCP (Netty) encoder/decoders (registry?)
            Keith Babo Master

            I was in the middle of creating an example app for you when I saw you reply. :-)  Thanks very much for posting the results of your investigation back to the forum.  That's a big help to others.  For posterity, I have attached the example I slapped together.  Some additional comments on your second post below ...

            you can not use existing spring dsl (xml) configurations with switchyard when working with camel components like netty (TCP/UDP).

            You can use the XML DSL for route definitions, but it's only the <routes> element and below.  In other words, it is not a complete Spring application context.

            instead, you must use CDI, requiring java code.  (can not do it simply through configuration, even if there happens to already be an appropriate codec already in the classpath...but a custom codec would always require classpath/coding anyway).

            This is correct.

            The CDI registry in Switchyard seems to be equiv of the camel registry.

            This is the case.  The bean registry in Camel is pluggable and we wire in the CDI Bean Manager as the registry provider.

             

            One note on the use of @Produces for the encoder/decoder beans.  This is the route that I prefer in this scenario, since there will likely be multiple encoders/decoders and this allows you to group them all in a single class.  An alternate strategy would be to have a distinct class which provides an implementation of ChannelUpstreamHandler/ChannelDownstreamHandler and has an @Named on the class definition.  Personally, I prefer @Produces.

            1 of 1 people found this helpful
            • 3. Re: Example for adding TCP (Netty) encoder/decoders (registry?)
              darren hartford Expert

              Thanks Keith for the confirmation, and as always the example you provided helps to remove assumptions that I may not have explained clearly (time well spent!).

               

              I've adjusted my comment around the spring dsl, as you are correct, it is more specific for configurations using references.

              • 4. Re: Example for adding TCP (Netty) encoder/decoders (registry?)
                darren hartford Expert

                Unfortunately, I have a followup question/scenario related to custom codecs --

                 

                The above work great with the netty-supplied encoders/decoders.

                 

                However, when I'm creating my own codecs, or even trying to use the existing DelimiterFrameDecoder with a special delimiter, I'm getting these kinds of problems:

                 

                ================

                at org.jboss.as.weld.WeldStartService.start(WeldStartService.java:63) [jboss-as-weld-7.2.0.Final-redhat-8.jar:7.

                2.0.Final-redhat-8]

                at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811)
                at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746)
                at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_45]
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_45]
                at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_45]

                Caused by: java.lang.NoClassDefFoundError: [Lorg/jboss/netty/buffer/ChannelBuffer;

                ...

                Caused by: java.lang.ClassNotFoundException: org.jboss.netty.buffer.ChannelBuffer from [Module "deployment.switchyard-customcodec.jar:main" from Service Module Loader]

                        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:196) [jboss-modules.jar:1.2.0.Final-redh

                at-1]

                ================

                usecase/test:

                @Produces

                    @Named ("pipeFrameDecoder")

                    ChannelHandlerFactory pipeFrameDecoderFactory() {

                final ChannelBuffer[] pipeDelimiter = new ChannelBuffer[] {
                ChannelBuffers.wrappedBuffer(new byte[] { '|' })
                };

                 

                return org.apache.camel.component.netty.ChannelHandlerFactories.newDelimiterBasedFrameDecoder(1000,pipeDelimiter);

                }

                 

                 

                It appears any custom code (in the switchyard project) that references Netty for ChannelBuffer, ChannelHandler, (replayingdecoder, onetoonedecoder,...) etc do not deploy as expected with the above classNotFoundException (while the IDE/development works without issue).

                • 5. Re: Example for adding TCP (Netty) encoder/decoders (registry?)
                  Keith Babo Master

                  You will need to add a dependency on the 'org.jboss.netty' module to your application.  You can do that with a MANIFEST.MF entry or by introducing a jboss-deployment-structure.xml file.  Details can be found here:

                  Class Loading in AS7 - JBoss AS 7.2 - Project Documentation Editor

                  1 of 1 people found this helpful
                  • 6. Re: Example for adding TCP (Netty) encoder/decoders (registry?)
                    darren hartford Expert

                    confirmed the maven approach -

                     

                    <plugin>

                           <groupId>org.apache.maven.plugins</groupId>

                           <artifactId>maven-jar-plugin</artifactId>

                           <configuration>

                              <archive>

                                 <manifestEntries>

                                    <Dependencies>org.jboss.netty</Dependencies>

                                 </manifestEntries>

                              </archive>

                           </configuration>

                         </plugin>

                     

                    It also updated the runtime server automatically (when using debug as/run as server).  Thank you Keith for the fast reply!