Thursday, January 29, 2015

Using Netty Component in Apache Camel - Hello World Example

This tutorial will demo how to use Netty Component in Apache Camel. By using netty component with camel in the example, this article is going to achieve these goals:

  • Create a TCP Socket server as camel endpoint with netty component, waiting for TCP client.
  • Utilize camel to route the received TCP message from Netty endpoint to other process logic
  • Send the processed result back to the TCP client.

In this example, every time server get a string, it will reply with a prefix "Hello" to form a greeting. If "Tom" is received from client, "Hello Tom" will be send back to the client.

This example also shows there features:

  • Use Camel and Netty component with Spring framework.
  • Use POJO class for business logic, which does not depend on any Camel or Netty componenent APIs.

1. What you need

  • JDK 1.7+
  • Maven 3.2.1

The dependencies will be managed by Maven.

2. Configure the maven pom.xml

The key artifacts are camel-core, camel-spring, camel-netty. The camel-spring artifact is needed for using Spring framework. The camel-netty artifact is needed for using netty component in camel.

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.shengwang.example</groupId>
<artifactId>spring-camel-netty3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-camel-netty</name>
<url>http://maven.apache.org</url>

<properties>
<!-- Generic properties -->
<java.version>1.7</java.version>

<!-- Camel -->
<camel.version>2.14.1</camel.version>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-netty</artifactId>
<version>${camel.version}</version>
</dependency>
</dependencies>


<build>
<plugins>
<!-- Use JDK 1.7 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>

<!-- Allows the routes to be run via 'mvn camel:run' -->
<plugin>
<groupId>org.apache.camel</groupId>
<artifactId>camel-maven-plugin</artifactId>
<version>${camel.version}</version>
</plugin>

</plugins>
</build>
</project>

In the POM file, Jave version and Camel version are defined in properties, followed by 3 dependencies. Then there are 2 plugins, the first one is to specify the Java version for building the project, the second plugin is needed to allow the camel route to be run without explicitly defined main function. It's very handy for demo and test.


3. Define a Java class to process the received message


This class is the process logic in this demo. This class is a POJO which means the business code is completely decoupled with camel or camel-netty component.

package com.shengwang.example.server;

import org.springframework.stereotype.Service;

@Service
public class EchoService {
public String sayHello(String guestName) {
System.out.println("Input guestName : "+ guestName);
return "Hello " + guestName;
}
}

The EchoService.java is very short and simple. Annotation @Service means it's will be registered in Spring context as a bean. How to use this bean will be explained later.


4. Define Camel route


Now Let's define another Java class for Camel route. This class extends RouteBuilder from Camel package and override the configure() method. In configure(), route is defined.

package com.shengwang.example.server;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.Main;

public class ServerRoute extends RouteBuilder {
public static void main(String[] args) throws Exception {
new Main().run(args);
}

@Override
public void configure() throws Exception {
from("netty:tcp://localhost:7000?sync=true&allowDefaultCodec=false&encoder=#stringEncoder&decoder=#stringDecoder")
.to("bean:echoService");
}
}

There are two methods in the ServerRoute class. The first one is a main, so we can run the route directly with the helper class Main from camel-spring package.


The second method has the route defined. It means somehow like "from to ". In this tutorial we get message from netty endpoint and send to a Spring bean which defined in previous section. The is how the EchoService class is used in this demo. The interesting part is the long from URI:"netty:tcp://localhost:7000?sync=true&allowDefaultCodec=false&encoder=#stringEncoder&decoder=#stringDecoder". It can be break down bit by bit.


"netty:tcp://localhost:7000" means netty component is needed to setup a tcp server on localhost port 7000.


"sync=true" means exchange pattern is InOut, the server will send back the response to client.


"allowDefaultCodec=false" means we don't use default encoder and decoder for netty component. Because in reality, the message come in netty endpoint is most unlikely formed in that message format, so it need to be set to false against its default value true.


"encoder=#stringEncoder&decoder=#stringDecoder" means message comes into netty will be decoded before transferring to the business logic EchoService, and response from EchoService will be encoded before sending back to the client. In this tutorial, server receive and send back Java string, so the StringEncoder and StringDecoder from camel-netty package will be used. The hash tag "#" refer to beans in Spring context which will be defined in Spring configuration file later.


5. Spring configuration


The Spring configuration file app-context.xml is located in directory src/main/resources/META-INF/spring.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd">

<context:component-scan base-package="com.shengwang.example" />

<!-- ================================== -->
<!-- Define camel context -->
<!-- ================================== -->
<camel:camelContext id="camel">
<!-- Location for route scan -->
<camel:package>com.shengwang.example.server</camel:package>
</camel:camelContext>

<!-- ================================== -->
<!-- Define netty codecs -->
<!-- ================================== -->
<!-- ChannelBuffer/ByteBuf to String -->
<bean id="stringDecoder" class="org.jboss.netty.handler.codec.string.StringDecoder"/>

<!-- String to ChannelBuffer/ByteBuf -->
<bean id="stringEncoder" class="org.jboss.netty.handler.codec.string.StringEncoder"/>

</beans>

<context:component-scan> scan for normal Spring beans of a package. Here is the EchoServer. Then,<camel:camelContext>, Camel context is defined so does the location of the route. Finally, encoder and decoder beans are defined.


Now all 4 files of the project have been defined. The directory structure looks like this.


Snap_2015.01.27 13.29.23_001


6. Run the server


There are 2 options to run this demo. The first one is to run the main in ServerRouter.java class. The Second is run via command line "mvn camel:run".  The latter way, which needs a maven plugin list at the bottom of the pom file,  is very convenient for test and development.


7. Test with a TCP client


Use telnet command as test client, connect to port 7000 with command "telnet localhost 7000". Anytime a key is pressed, a TCP message will send out to the demo server  and a reply will come back immediately.


Appendix


The netty component camel-netty is based on netty 3.x version. If a netty component based on netty 4.x is preferred, use camel-netty4 component instead.

Powered by Blogger.

About The Author

My Photo

Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List