Advanced Techniques

This section covers advanced techniques that you may find to be helpful in certain situations.

Strategy Interfaces

In many cases, the configuration described earlier is all that is needed to enable secure communication over TCP/IP. However, Spring Integration provides a number of strategy interfaces to allow customization and modification of socket factories and sockets:

  • TcpSSLContextSupport

  • TcpSocketFactorySupport

  • TcpSocketSupport

  • TcpNetConnectionSupport

  • TcpNioConnectionSupport

The TcpSSLContextSupport Strategy Interface

The following listing shows the TcpSSLContextSupport strategy interface:

public interface TcpSSLContextSupport {

    SSLContext getSSLContext() throws Exception;

}

Implementations of the TcpSSLContextSupport interface are responsible for creating an SSLContext object. The implementation provided by the framework is the DefaultTcpSSLContextSupport, described earlier. If you require different behavior, implement this interface and provide the connection factory with a reference to a bean of your class' implementation.

The TcpSocketFactorySupport Strategy Interface

The following listing shows the definition of the TcpSocketFactorySupport strategy interface:

public interface TcpSocketFactorySupport {

    ServerSocketFactory getServerSocketFactory();

    SocketFactory getSocketFactory();

}

Implementations of this interface are responsible for obtaining references to ServerSocketFactory and SocketFactory. Two implementations are provided. The first is DefaultTcpNetSocketFactorySupport for non-SSL sockets (when no ssl-context-support attribute is defined). This uses the JDK’s default factories. The second implementation is DefaultTcpNetSSLSocketFactorySupport. By default, this is used when an ssl-context-support attribute is defined. It uses the SSLContext created by that bean to create the socket factories.

This interface applies only if using-nio is false. NIO does not use socket factories.

The TcpSocketSupport Strategy Interface

The following listing shows the definition of the TcpSocketSupport strategy interface:

public interface TcpSocketSupport {

    void postProcessServerSocket(ServerSocket serverSocket);

    void postProcessSocket(Socket socket);

}

Implementations of this interface can modify sockets after they are created and after all configured attributes have been applied but before the sockets are used. This applies whether you use NIO or not. For example, you could use an implementation of this interface to modify the supported cipher suites on an SSL socket, or you could add a listener that gets notified after SSL handshaking is complete. The sole implementation provided by the framework is the DefaultTcpSocketSupport, which does not modify the sockets in any way.

To supply your own implementation of TcpSocketFactorySupport or TcpSocketSupport, provide the connection factory with references to beans of your custom type by setting the socket-factory-support and socket-support attributes, respectively.

The TcpNetConnectionSupport Strategy Interface

The following listing shows the definition of the TcpNetConnectionSupport strategy interface:

public interface TcpNetConnectionSupport {

    TcpNetConnection createNewConnection(Socket socket,
            boolean server, boolean lookupHost,
            ApplicationEventPublisher applicationEventPublisher,
            String connectionFactoryName) throws Exception;

}

This interface is invoked to create objects of type TcpNetConnection (or its subclasses). The framework provides a single implementation (DefaultTcpNetConnectionSupport), which, by default, creates simple TcpNetConnection objects. It has two properties: pushbackCapable and pushbackBufferSize. When push back is enabled, the implementation returns a subclass that wraps the connection’s InputStream in a PushbackInputStream. Aligned with the PushbackInputStream default, the buffer size defaults to 1. This lets deserializers “unread” (push back) bytes into the stream. The following trivial example shows how it might be used in a delegating deserializer that “peeks” at the first byte to determine which deserializer to invoke:

public class CompositeDeserializer implements Deserializer<byte[]> {

    private final ByteArrayStxEtxSerializer stxEtx = new ByteArrayStxEtxSerializer();

    private final ByteArrayCrLfSerializer crlf = new ByteArrayCrLfSerializer();

    @Override
    public byte[] deserialize(InputStream inputStream) throws IOException {
        PushbackInputStream pbis = (PushbackInputStream) inputStream;
        int first = pbis.read();
        if (first < 0) {
            throw new SoftEndOfStreamException();
        }
        pbis.unread(first);
        if (first == ByteArrayStxEtxSerializer.STX) {
            this.receivedStxEtx = true;
            return this.stxEtx.deserialize(pbis);
        }
        else {
            this.receivedCrLf = true;
            return this.crlf.deserialize(pbis);
        }
    }

}

The TcpNioConnectionSupport Strategy Interface

The following listing shows the definition of the TcpNioConnectionSupport strategy interface:

public interface TcpNioConnectionSupport {

    TcpNioConnection createNewConnection(SocketChannel socketChannel,
            boolean server, boolean lookupHost,
            ApplicationEventPublisher applicationEventPublisher,
            String connectionFactoryName) throws Exception;

}

This interface is invoked to create TcpNioConnection objects (or objects from subclasses). Spring Integration provides two implementations: DefaultTcpNioSSLConnectionSupport and DefaultTcpNioConnectionSupport. Which one is used depends on whether SSL is in use. A common use case is to subclass DefaultTcpNioSSLConnectionSupport and override postProcessSSLEngine. See the SSL client authentication example. As with the DefaultTcpNetConnectionSupport, these implementations also support push back.

Example: Enabling SSL Client Authentication

To enable client certificate authentication when you use SSL, the technique depends on whether you use NIO. When you do not NIO , provide a custom TcpSocketSupport implementation to post-process the server socket:

serverFactory.setTcpSocketSupport(new DefaultTcpSocketSupport() {

    @Override
    public void postProcessServerSocket(ServerSocket serverSocket) {
        ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
    }

});

(When you use XML configuration, provide a reference to your bean by setting the socket-support attribute).

When you use NIO, provide a custom TcpNioSslConnectionSupport implementation to post-process the SSLEngine, as the following example shows:

@Bean
public DefaultTcpNioSSLConnectionSupport tcpNioConnectionSupport() {
    return new DefaultTcpNioSSLConnectionSupport(serverSslContextSupport) {

            @Override
            protected void postProcessSSLEngine(SSLEngine sslEngine) {
                sslEngine.setNeedClientAuth(true);
            }

    }
}

@Bean
public TcpNioServerConnectionFactory server() {
    ...
    serverFactory.setTcpNioConnectionSupport(tcpNioConnectionSupport());
    ...
}

(When you use XML configuration, since version 4.3.7, provide a reference to your bean by setting the nio-connection-support attribute).