3

I'm looking for an example to connect TCP through sping boot without xml(spring-integration).

I got the following snippet from How to create a Tcp Connection in spring boot to accept connections? URL.

in this example, just main method alone enough to connect tcp. why other beans and the transformer are declared here?

Is it wrong? Instead of using simple Java socket client to accept the response, I would like to integrate with Spring. But no suitable examples available using Java DSL.

Could you please help?

package com.example;

import java.net.Socket;

import javax.net.SocketFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory;
import org.springframework.integration.transformer.ObjectToStringTransformer;
import org.springframework.messaging.MessageChannel;

@SpringBootApplication
public class So39290834Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So39290834Application.class, args);
        Socket socket = SocketFactory.getDefault().createSocket("localhost", 9999);
        socket.getOutputStream().write("foo\r\n".getBytes());
        socket.close();
        Thread.sleep(1000);
        context.close();
    }

    @Bean
    public TcpNetServerConnectionFactory cf() {
        return new TcpNetServerConnectionFactory(9999);
    }

    @Bean
    public TcpReceivingChannelAdapter inbound(AbstractServerConnectionFactory cf) {
        TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
        adapter.setConnectionFactory(cf);
        adapter.setOutputChannel(tcpIn());
        return adapter;
    }

    @Bean
    public MessageChannel tcpIn() {
        return new DirectChannel();
    }

    @Transformer(inputChannel = "tcpIn", outputChannel = "serviceChannel")
    @Bean
    public ObjectToStringTransformer transformer() {
        return new ObjectToStringTransformer();
    }

    @ServiceActivator(inputChannel = "serviceChannel")
    public void service(String in) {
        System.out.println(in);
    }

}
Shakthi
  • 826
  • 3
  • 15
  • 33

1 Answers1

3

This application is both the client and the server.

That question was specifically about how to write the server side (accept the connection), using Spring Integration.

The main() method is simply a test that connects to the server side. It uses standard Java sockets APIs; it could also have been written to use Spring Integration components on the client side.

BTW, you don't have to use XML to write a Spring Integration application, you can configure it with annotations, or use the Java DSL. Read the documentation.

EDIT

Client/Server Example using Java DSL

@SpringBootApplication
public class So54057281Application {

    public static void main(String[] args) {
        SpringApplication.run(So54057281Application.class, args);
    }

    @Bean
    public IntegrationFlow server() {
        return IntegrationFlows.from(Tcp.inboundGateway(
                    Tcp.netServer(1234)
                        .serializer(codec()) // default is CRLF
                        .deserializer(codec()))) // default is CRLF
                .transform(Transformers.objectToString()) // byte[] -> String
                .<String, String>transform(p -> p.toUpperCase())
                .get();
    }

    @Bean
    public IntegrationFlow client() {
        return IntegrationFlows.from(MyGateway.class)
                .handle(Tcp.outboundGateway(
                    Tcp.netClient("localhost", 1234)
                        .serializer(codec()) // default is CRLF
                        .deserializer(codec()))) // default is CRLF
                .transform(Transformers.objectToString()) // byte[] -> String
                .get();
    }

    @Bean
    public AbstractByteArraySerializer codec() {
        return TcpCodecs.lf();
    }

    @Bean
    @DependsOn("client")
    ApplicationRunner runner(MyGateway gateway) {
        return args -> {
            System.out.println(gateway.exchange("foo"));
            System.out.println(gateway.exchange("bar"));
        };
    }

    public interface MyGateway {

        String exchange(String out);

    }

}

result

FOO
BAR
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Could you please share the snippet of server and client separately for TCP using spring integration? Also suggest me which is best to consume TCP service? Is Socket enough or spring itegration is better? I don't have much experience in spring integration. – Shakthi Jan 06 '19 at 09:19
  • It's your choice; you can do the low-level socket coding yourself, or use something like Spring Integration to provide a higher-level abstraction. It really depends on exactly what kind of communication you need. I added a Spring Integration client/server example using the Java DSL. – Gary Russell Jan 06 '19 at 16:37
  • Russel, The above snippet works well. If I call the MyGateway from other service like @Autowired MyGateway gateway; by moving the interface to some other package then its not working. It says `Consider defining a bean of type MyGateway`. Please Suggest. – Shakthi Jan 10 '19 at 20:53
  • 1
    Notice the `@DependsOn`. Anything that uses a gateway implementation generated by an Integration flow needs it so the flow is created first. – Gary Russell Jan 10 '19 at 21:00
  • the above code is working only if I comment `.serializer(codec())` and the `.deserializer(codec())` lines. That too, for one api I'm getting response and for the another API I get error like `org.springframework.messaging.MessagingException: Exception while awaiting reply; nested exception is java.io.IOException: CRLF not found before max message length: 2048` this exception. What should I do? – Shakthi Jan 11 '19 at 15:54
  • I added the `codec()` to show how to override the default; if your server is expecting (and returns CRLF) then use a `ByteArrayCrLfSerializer` (which is the default). You can increase the max message length with `setMaxMessageSize(...)`; the size should be large enough for the largest expected message, while not wasting memory; the default is 2048 bytes. – Gary Russell Jan 11 '19 at 16:02
  • 1
    It works fine once I increased the `setMaxMessageSize(...)`. Thank you :) – Shakthi Jan 11 '19 at 18:38