Skip to content

Commit

Permalink
Merge pull request spring-projects#78 from ghillert/INTSAMPLES-63
Browse files Browse the repository at this point in the history
* ghillert-INTSAMPLES-63:
  INTSAMPLES-63 - Polish Tcp-Client-Server Sample
  • Loading branch information
ghillert committed Jan 10, 2013
2 parents 863f5ee + 563e087 commit 7928871
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 206 deletions.
128 changes: 108 additions & 20 deletions basic/tcp-client-server/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,68 @@
TCP Sample
==========

This is a place to get started with the [Transmission Control Protocol][] (TCP). It demonstrates a simple message flow represented by the diagram below:
This is the place to get started with the Samples for *Spring Integration's* support for the the [Transmission Control Protocol][] (TCP). The sample demonstrates a simple message flow represented by the diagram below:

Gateway -> Channel -> TcpOutboundGateway -> <===Socket===> -> TcpInboundGateway -> Channel -> ServiceActivator
Gateway (SimpleGateway) -> Channel -> TcpOutboundGateway -> <==Socket==> -> TcpInboundGateway -> Channel -> ServiceActivator (EchoService)

The service returns a response which the *Inbound Gateway* sends back over the socket to the *Outbound Gateway* and the result is returned to the client that invoked the original **SimpleGateway** method.
The *EchoService* class returns a response which the *Inbound Gateway* sends back over the socket to the *Outbound Gateway* and the result is returned to the client that invoked the original **SimpleGateway** method.

## Running the Sample
Several variations of the sample are provided:

To run sample simply execute a test case in the **org.springframework.integration.samples.tcpclientserver** package.
* Client-Server Demo with explicit Transformers (Sample also provides [Telnet][] connectivity)
* Client-Server Demo with ConversionService
* Serializer Demo
* Using the Stx-Etx [Serializer][]/[Deserializer][]
* Using a Custom [Serializer][]/[Deserializer][]

Note that the test case includes an alternative configuration that uses the built-in *conversion service* and the channel *dataType* attribute, instead of explicit *Transformers*, to convert from byte arrays to Strings.
### Client-Server Demo

Simply change the *@ContextConfiguration* to switch between the two techniques. In addition, a simple telnet server is provided; see **TelnetServer** in *src/main/java*. Run this class as a Java application and then use `telnet` to connect to the service (**telnet localhost 11111**).
The Client-Server Demo illustrates the use of a *Gateway* as an entry point into the integration flow. The message generated by the *Gateway* is sent over *TCP* by the *Outbound Gateway* to the *Inbound Gateway*. In turn the *Inbound Gateway* sends the message to an echo service (Class *EchoService*) and the echoed response comes back over *TCP*. The demo uses explicit *Transformer*s to convert the *byte array* payloads to *Strings*.

Messages sent will be returned, preceded by 'echo:'.
You can execute this sample simply via Maven:

$ mvn clean package exec:java

Alternatively, you can also execute the **Main** method in class *org.springframework.integration.samples.tcpclientserver.Main*. In both cases you should see the following console output:

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

Welcome to the Spring Integration
TCP-Client-Server Sample!

For more information please visit:
http://www.springintegration.org/

=========================================================
Detect open server socket...using port 5680
Waiting for server to accept connections...running.


Please enter some text and press <enter>:
Note:
- Entering FAIL will create an exception
- Entering q will quit the application

--> Please also check out the other samples, that are provided as JUnit tests.
--> You can also connect to the server on port '5680' using Telnet.


hello
echo:hello
q
Exiting application...bye.

The respective open server socket it dynamically selected. Alternatively, you can also customize the port by providing an additional system property at startup e.g.

$ mvn clean package exec:java -DavailableServerSocket=7777

#### Connect via Telnet

The configured *Inbound Gateway* processes [CRLF][] (Carriage Return + Line Feed) delimited messages using the default [ByteArrayCrLfSerializer][]. This means that the *Inbound Gateway* works works fine as a very simple [Telnet][] server! Just start up the sample and connect to the used port:

$ telnet localhost <your_port>

Each time you hit enter you should see your input echoed back, preceded by 'echo:'

$ telnet localhost 11111
Trying 127.0.0.1...
Expand All @@ -26,33 +73,74 @@ Messages sent will be returned, preceded by 'echo:'.
Test
echo:Test
^]

telnet> quit
Connection closed.

>Note that the test case also demonstrates error handling on an inbound gateway using direct channels. If the payload is 'FAIL', the EchoService throws an exception. The gateway is configured >with an error-channel attribute. Messages sent to that channel are consumed by a transformer that concatenates the inbound message payload with the message text from the thrown exception, >returning **FAIL:Failure Demonstration** over the TCP socket.
> In order to quit the Telnet session, press `Ctrl+]` (Windows) or `Control+]` (Mac) followed by `q` or `quit`.
This can also be demonstrated with the telnet client thus...
The test case also demonstrates error handling on an *Inbound Gateway* using direct channels. If the payload is 'FAIL', the *EchoService* throws an exception. The *Gateway* is configured with an **error-channel** attribute. Messages sent to that channel are consumed by a *Transformer* that concatenates the inbound message payload with the message text from the thrown exception, returning **FAIL:Failure Demonstration** over the *TCP* socket.

$ telnet localhost 11111
This can also be demonstrated with the Telnet client:

telnet 127.0.0.1 5679
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello world!
echo:Hello world!
hello
echo:hello
FAIL
FAIL:Failure Demonstration
Hello
echo:Hello
Hello World
echo:Hello World
^]

telnet> quit
Connection closed.

A third option exists for converting a stream of bytes to a domain object or message payload. You can hook up different serializers/deserializers at the connection factory which will apply the conversions right when the stream comes in to the *Gateway* and right when it goes out.
### Client-Server Demo with ConversionService

This demo is similar to the previous demo. However, instead of using explicit *Transformers*, this version shows how Spring's [Conversion Service][] can be used to convert the *byte array* payloads to Strings. This demo also uses a *Gateway* as an entry point into the integration flow. The message generated by the *Gateway* is sent over *TCP* by the *Outbound Gateway* to the *Inbound Gateway*. In return, the *Inbound Gateway* sends the message to the echo service and the echoed response comes back over *TCP* and is returned to the test case for verification.

> Please note the channel's *dataType* attribute. This attribute which will trigger the *conversion service*.
<int:channel id="toSA" datatype="java.lang.String" />

You can run the example by executing JUnit test **TcpClientServerDemoWithConversionServiceTest**.

### Serializer Demo

A third option exists for converting a stream of bytes to a domain object or message payload. You can hook up a different [Serializer][]/[Deserializer][] at the connection factory:

<int-ip:tcp-connection-factory id="…"
serializer=""
deserializer=""/>

This will apply the conversions right when the stream comes in to the *Gateway* and right when it goes out. Two examples (JUnit Tests) are provided:

* **TcpServerConnectionDeserializeTest** for using a simple (comes with Spring) Stx/Etx [Serializer][]/[Deserializer][].
* **TcpServerCustomSerializerTest** for creating and using your own serializers

#### Stx-Etx Serializer Demo

The Stx-Etx Serializer Demo shows an example of using the *Stx/Etx stream framing serializer* ([ByteArrayStxEtxSerializer][]) that is included with *Spring Integration*. This serializer reads data in an *InputStream* to a *byte array*. The data must be prefixed with the `<stx>` [control character][] and terminated by the `<etx>` [control character][].

We can be confident that the streams are properly handled because we explicitly send a stream with the Stx/Etx frame and the beginning and end of the actual content and the Server is configured to be able to handle the frame. In the asserts, we assert that the payload, once it reaches a component (in this case, the message listener we create and attach to the *incomingServerChannel*), does not have any of the Stx/Etx bytes.

You can run the example by executing JUnit test **TcpServerConnectionDeserializeTest**.

#### Using Custom Serializers

Some use cases may dictate you needing to create your own stream handling serializers and deserializers. This sample shows a custom [Serializer][]/[Deserializer][] being used with the Java socket API on the front end (client) and the Spring Integration TCP inbound gateway with the custom serializer/deserializers.

See **TcpServerConnectionDeserializeTest** for using a simple (comes with spring) Stx/Etx serializer.
You can run the example by executing JUnit test **TcpServerCustomSerializerTest**.

See **TcpServerCustomSerializerTest** for creating and using your own serializers

[ByteArrayCrLfSerializer]: http://static.springsource.org/spring-integration/api/org/springframework/integration/ip/tcp/serializer/ByteArrayCrLfSerializer.html
[ByteArrayStxEtxSerializer]: http://static.springsource.org/spring-integration/api/org/springframework/integration/ip/tcp/serializer/ByteArrayStxEtxSerializer.html
[control character]: http://en.wikipedia.org/wiki/Control_character
[Conversion Service]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/core/convert/ConversionService.html
[CRLF]: http://en.wikipedia.org/wiki/Newline
[Deserializer]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/core/serializer/Deserializer.html
[Serializer]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/core/serializer/Serializer.html
[Telnet]: http://en.wikipedia.org/wiki/Telnet
[Transmission Control Protocol]: http://en.wikipedia.org/wiki/Transmission_Control_Protocol
9 changes: 9 additions & 0 deletions basic/tcp-client-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<spring.integration.version>2.2.0.RELEASE</spring.integration.version>
<log4j.version>1.2.17</log4j.version>
<junit.version>4.10</junit.version>
<java.main.class>org.springframework.integration.samples.tcpclientserver.Main</java.main.class>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -75,6 +76,14 @@
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>${java.main.class}</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,37 @@
import org.apache.commons.lang.builder.ToStringStyle;

/**
* @author: ceposta
* @author Christian Posta
* @author Gunnar Hillert
*/
public class CustomOrder {
private int number;
private String sender;
private String message;

public CustomOrder(int number, String sender) {
this.number = number;
this.sender = sender;
}

public int getNumber() {
return number;
}

public String getSender() {
return sender;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
private int number;
private String sender;
private String message;

public CustomOrder(int number, String sender) {
this.number = number;
this.sender = sender;
}

public int getNumber() {
return number;
}

public String getSender() {
return sender;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
Loading

0 comments on commit 7928871

Please sign in to comment.