Spring Integration TcpInboundGateway sending conditional reply - spring-integration

I have configured TcpInboundGateway to receive requests from client and my configuration is as follows. So as per below configuration every client requested is responded back,but what i want is response should be send back only if certain condition is true,not the every time, what changes needs to be done in configuration?
#SpringBootApplication
#IntegrationComponentScan
public class SpringIntegrationApplication extends SpringBootServletInitializer{
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringIntegrationApplication.class, args);
System.in.read();
ctx.close();
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringIntegrationApplication.class);
}
private static Class<SpringIntegrationApplication> applicationClass = SpringIntegrationApplication.class;
#Bean
TcpNetServerConnectionFactory cf(){
TcpNetServerConnectionFactory connectionFactory=new TcpNetServerConnectionFactory(8765);
return connectionFactory;
}
#Bean
TcpInboundGateway tcpGate(){
TcpInboundGateway gateway=new TcpInboundGateway();
gateway.setConnectionFactory(cf());
gateway.setRequestChannel(requestChannel());
return gateway;
}
#Bean
public MessageChannel requestChannel(){
return new DirectChannel();
}
#MessageEndpoint
public class Echo {
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#SuppressWarnings("deprecation") #Header("ip_address") String ip){
byte[] rawbytes = gosDataSerivce.byteArrayToHex(in,ip);//Process bytes and returns result
return rawbytes;
}
}
}

Not sure where is your problem, but you can just simply return null from your echo(). In that case the ServiceActivatingHandler doesn't care and stops it work. Just because of requiresReply = false.
From other side the TcpInboundGateway doesn't care about null, too:
Message<?> reply = this.sendAndReceiveMessage(message);
if (reply == null) {
if (logger.isDebugEnabled()) {
logger.debug("null reply received for " + message + " nothing to send");
}
return false;
}
That is possible because of replyTimeout option for the MessagingTemplate on the background. By default it is 1 sec. After that the sendAndReceiveMessage() just returns null to the caller.
You can adjust this option on the TcpInboundGateway.

Related

Spring integration migration to Redis from RabbitMQ to share application events

We are migrating to Redis from RabbitMQ in our microservice applications.
Here is our service activator
#ServiceActivator(inputChannel = ApplicationEventChannelNames.REMOTE_CHANNEL)
public void handleApplicationEvent(#Header(value = ApplicationEventHeaders.APPLICATION_EVENT) final ApplicationEvent event,
#Payload Object message) {
...
}
Initially we had a problem where we were losing application event in the SimpleMessageConverter. We solved it by implementing a CustomRedisMessageConverter and putting application event into the payload in the fromMessage method and retreiving it from payload and create a new message headers with application event in the toMessage method.
#Override
public Object fromMessage(Message<?> message, Class<?> targetClass) {
if (message.getHeaders().get(ApplicationEventHeaders.APPLICATION_EVENT) != null) {
Map<String, Object> map = new HashMap<>();
map.put("headers", ((ApplicationEvent) message.getHeaders().get(ApplicationEventHeaders.APPLICATION_EVENT)).getName());
map.put("payload", message.getPayload());
GenericMessage<Map<String, Object>> msg = new GenericMessage<>(map, message.getHeaders());
return super.fromMessage(msg, targetClass);
}
return super.fromMessage(message, targetClass);
}
#Override
public Message<?> toMessage(Object payload, MessageHeaders headers) {
try {
final Map<String, ?> message = new ObjectMapper().readValue((String) payload, new TypeReference<Map<String, ?>>() {});
if (message.get("headers") != null) {
final Map<String, Object> messageHeaders = new HashMap<>(headers);
messageHeaders.put(ApplicationEventHeaders.APPLICATION_EVENT, new ApplicationEvent((String) message.get("headers")));
return super.toMessage(message.get("payload"), new MessageHeaders(messageHeaders));
}
} catch (JsonProcessingException exception) {
/* Intentionally left blank */
}
return super.toMessage(payload, headers);
}
We are wondering if there is a better approach for doing this?
Lastly, payload in the service activator come as a LinkedHashMap but we want it to be an object. With RabbitMQ this was handled.
Is there any way to do the same in Redis? Or do we use headers to keep track of the type of a payload and manually convert them into an object?
UPDATE - REDIS Configuration
#Bean
public RedisInboundChannelAdapter applicationEventInboundChannelAdapter(#Value(value = "${com.xxx.xxx.xxx.integration.spring.topic}") String topic,
MessageChannel applicationEventRemoteChannel,
RedisConnectionFactory connectionFactory) {
final RedisInboundChannelAdapter inboundChannelAdapter = new RedisInboundChannelAdapter(connectionFactory);
inboundChannelAdapter.setTopics(topic);
inboundChannelAdapter.setOutputChannel(applicationEventRemoteChannel);
inboundChannelAdapter.setErrorChannel(errorChannel());
inboundChannelAdapter.setMessageConverter(new CustomRedisMessageConverter());
return inboundChannelAdapter;
}
#ServiceActivator(inputChannel = "errorChannel")
public void processError(MessageHandlingException exception) {
try {
logger.error(
"Could not process {}, got exception: {}",
exception.getFailedMessage().getPayload(),
exception.getMessage());
logger.error(
ExceptionUtils.readStackTrace(exception));
} catch (Throwable throwable) {
logger.error(
"Got {} during processing with message: {} ",
MessageHandlingException.class.getSimpleName(),
exception);
}
}
#Bean
#ServiceActivator(inputChannel = ApplicationEventChannelNames.LOCAL_CHANNEL)
public RedisPublishingMessageHandler redisPublishingMessageHandler(#Value(value = "${com.xxx.xxx.xxx.integration.spring.topic}") String topic,
RedisConnectionFactory redisConnectionFactory) {
final RedisPublishingMessageHandler redisPublishingMessageHandler = new RedisPublishingMessageHandler(redisConnectionFactory);
redisPublishingMessageHandler.setTopic(topic);
redisPublishingMessageHandler.setSerializer(new Jackson2JsonRedisSerializer<>(String.class));
redisPublishingMessageHandler.setMessageConverter(new CusomRedisMessageConverter());
return redisPublishingMessageHandler;
}
/*
* MessageChannel
*/
#Bean
public MessageChannel errorChannel() {
return new DirectChannel();
}
Redis does not support headers, so you have to embed them into a body. See EmbeddedJsonHeadersMessageMapper which could be supplied into that org.springframework.integration.support.converter.SimpleMessageConverter on both side.

How to config tcp server to receive data from multiple client using spring boot?

I would like to configure TCP server to receive and reply data from multiple clients. I searched many other thread but could not found exact way to do. I'm using spring integration first time and have no experience.
Server requirement
should be able to receive and reply data to specific client (can have multiple client, each client should processed separately)
should be able to send data to client and wait for response for specific timeout.
Should be able to detect client is disconnect or not. if Client is disconnect then connection should be closed to save memory. (In earlier method without spring integration I was able to do it by ping client and see sending is failed or not but don't know how to do with spring integration)
I tried below code, In which I'm able to send data to client but could achieve my above requirements
TCP Server Configuration:
#Configuration
public class TcpServerConfig {
private List<TcpConnectionOpenEvent> clientList = new ArrayList<>();
public List<TcpConnectionOpenEvent> getClientList() {
return clientList;
}
#Bean
public TcpReceivingChannelAdapter server(TcpNetServerConnectionFactory cf) {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(cf);
adapter.setOutputChannel(inputChannel());
return adapter;
}
#Bean
public MessageChannel inputChannel() {
return new QueueChannel();
}
#Bean
public MessageChannel outputChannel() {
return new DirectChannel();
}
#Bean
public TcpNetServerConnectionFactory cf() {
return new TcpNetServerConnectionFactory(1001);
}
#Bean
public IntegrationFlow outbound() {
return IntegrationFlows.from(outputChannel())
.handle(sender())
.get();
}
#Bean
public MessageHandler sender() {
TcpSendingMessageHandler tcpSendingMessageHandler = new TcpSendingMessageHandler();
tcpSendingMessageHandler.setConnectionFactory(cf());
return tcpSendingMessageHandler;
}
#Bean
public ApplicationListener<TcpConnectionOpenEvent> listener() {
return new ApplicationListener<TcpConnectionOpenEvent>() {
#Override
public void onApplicationEvent(TcpConnectionOpenEvent event) {
outputChannel().send(MessageBuilder.withPayload("foo")
.setHeader(IpHeaders.CONNECTION_ID, event.getConnectionId())
.build());
clientList.add(event);
}
};
}
}
Test Code:
#Service
public class Test {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageServiceImpl.class);
#Autowired
TcpServerConfig tcpServerConfig;
#Autowired
private MessageChannel outputChannel;
#Autowired
private MessageChannel inputChannel;
#Scheduled(fixedRate = 1000)
void task() {
LOGGER.info("Client count: " + tcpServerConfig.getClientList().size());
for (TcpConnectionOpenEvent client : tcpServerConfig.getClientList()) {
outputChannel.send(MessageBuilder.withPayload("foo")
.setHeader(IpHeaders.CONNECTION_ID, client.getConnectionId())
.build());
}
}
}
Any help would be appreciated.
Here is one solution:
#SpringBootApplication
#EnableScheduling
public class So62877512ServerApplication {
public static void main(String[] args) {
SpringApplication.run(So62877512ServerApplication.class, args);
}
#Bean
public IntegrationFlow serverIn(Handler handler) {
return IntegrationFlows.from(Tcp.inboundAdapter(server()))
.transform(Transformers.objectToString())
.filter(handler, "existingConnection", spec -> spec
.discardFlow(f -> f
.handle(handler, "sendInitialReply")))
.handle(handler, "reply")
.get();
}
#Bean
public IntegrationFlow serverOut() {
return f -> f.handle(Tcp.outboundAdapter(server()));
}
#Bean
public TcpServerConnectionFactorySpec server() {
return Tcp.netServer(1234)
.serializer(TcpCodecs.lf())
.deserializer(TcpCodecs.lf()); // compatible with netcat
}
}
#Component
#DependsOn("serverOut")
class Handler {
private static final Logger LOG = LoggerFactory.getLogger(Handler.class);
private final ConcurrentMap<String, BlockingQueue<Message<?>>> clients = new ConcurrentHashMap<>();
private final MessageChannel out;
private final TcpNetServerConnectionFactory server;
public Handler(#Qualifier("serverOut.input") MessageChannel out, TcpNetServerConnectionFactory server) {
this.out = out;
this.server = server;
}
public boolean existingConnection(Message<?> message) {
String connectionId = message.getHeaders().get(IpHeaders.CONNECTION_ID, String.class);
boolean containsKey = this.clients.containsKey(connectionId);
if (!containsKey) {
this.clients.put(connectionId, new LinkedBlockingQueue<Message<?>>());
}
return containsKey;
}
public void sendInitialReply(Message<String> message) {
LOG.info("Replying to " + message.getPayload());
this.out.send(MessageBuilder.withPayload(message.getPayload().toUpperCase())
.copyHeaders(message.getHeaders()).build());
}
#Scheduled(fixedDelay = 5000)
public void sender() {
this.clients.forEach((key, queue) -> {
try {
this.out.send(MessageBuilder.withPayload("foo")
.setHeader(IpHeaders.CONNECTION_ID, key).build());
Message<?> reply = queue.poll(10, TimeUnit.SECONDS);
if (reply == null) {
LOG.error("Timeout waiting for " + key);
this.server.closeConnection(key);
}
else {
LOG.info("Reply " + reply.getPayload() + " from " + key);
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Interrupted");
}
catch (Exception e) {
LOG.error("Failed to send to " + key, e);
}
});
}
public void reply(Message<String> in) {
BlockingQueue<Message<?>> queue = this.clients.get(in.getHeaders().get(IpHeaders.CONNECTION_ID, String.class));
if (queue != null) {
queue.add(in);
}
}
#EventListener
public void closed(TcpConnectionCloseEvent event) {
this.clients.remove(event.getConnectionId());
LOG.info(event.getConnectionId() + " closed");
}
}
$ nc localhost 1234
foo <- typed
FOO
foo
bar <- typed
foo
bar <- typed
foo
$ <- closed by server - timeout
2020-07-14 14:41:04.906 INFO 64763 --- [pool-1-thread-2] com.example.demo.Handler : Replying to foo
2020-07-14 14:41:13.841 INFO 64763 --- [ scheduling-1] com.example.demo.Handler : Reply bar from localhost:65115:1234:a9fc7e3d-4dda-4627-b765-4f0bb0835153
2020-07-14 14:41:21.465 INFO 64763 --- [ scheduling-1] com.example.demo.Handler : Reply bar from localhost:65115:1234:a9fc7e3d-4dda-4627-b765-4f0bb0835153
2020-07-14 14:41:36.473 ERROR 64763 --- [ scheduling-1] com.example.demo.Handler : Timeout waiting for localhost:65115:1234:a9fc7e3d-4dda-4627-b765-4f0bb0835153
2020-07-14 14:41:36.474 INFO 64763 --- [ scheduling-1] com.example.demo.Handler : localhost:65115:1234:a9fc7e3d-4dda-4627-b765-4f0bb0835153 closed

Spring integration TCP/IP close connection problem

I use spring integration as a Full Duplex communication system's gateway module.
that flow is client app <--> spring-integration-ip-module (siid) <--> server app
The problem is when client app closed, ssid can't closed connection with server app side?
here is my code
// siid connet to client
#Bean
public TcpNetServerConnectionFactory server(){
TcpNetServerConnectionFactory server=new TcpNetServerConnectionFactory(1234);
server.setMapper(new TcpSerMapper()); // use 'mapper' attribute in XML
MySerializer mySeri=new MySerializer();
server.setDeserializer(mySeri);
server.setSerializer(mySeri);
return server;
}
// inboundGateway, inChannel as reqeustChannel
#Bean
public TcpInboundGateway inGate(){
TcpInboundGateway inGate=new TcpInboundGateway();
inGate.setConnectionFactory(server());
inGate.setRequestChannelName("inChannel");
inGate.setReplyChannelName("outputChannel");
return inGate;
}
// serviceActivator to get inChannel's payload msg and send though a gateway.
#ServiceActivator(inputChannel = "inChannel")
public byte[]doClientForward(Message<?> msg){
byte[]msgPayload=(byte[])(msg.getPayload());
byte[]sendResult=null;
ToTCP toTcp=(ToTCP)contextBean.get("toTcpBean"); // ToTCP is a gateway
sendResult=toTcp.sends((msgPayload),"localhost",7779);
QueueChannel outputChannel=(QueueChannel)contextBean.get("outputChannel");
return sendResult;
}
public static class DynamicSerSeri extends AbstractPooledBufferByteArraySerializer {
protected byte[] doDeserialize(InputStream inputStream, byte[] buffer) throws IOException {
byte[] bytes = this.copyBuffer(inputStream, buffer);
return bytes;
}
public void serialize(byte[] object, OutputStream outputStream) throws IOException {
outputStream.write(object);
}
public byte[] copyBuffer(InputStream inputStream, byte[] buffer) throws IOException {
int n = 0;
int bite = 0;
try {
while (true) {
bite = inputStream.read(); // blocked here
this.setMaxMessageSize(inputStream.available() + 1);
buffer = new byte[inputStream.available() + 1];
if (bite < 0 && n == 0) {
throw new SoftEndOfStreamException("Stream closed between payloads");
}
checkClosure(bite);
buffer[n++] = (byte) bite;
if (bite == -1) {
break;
}
if (n == this.maxMessageSize) {
break;
}
}
return buffer;
} catch (SoftEndOfStreamException e) {
throw e; // I was stuck here. when client closed, cf can't receive this exception and send close singnal to server side
} catch (IOException e) {
publishEvent(e, buffer, n);
throw e;
} catch (RuntimeException e) {
publishEvent(e, buffer, n);
throw e;
}
}
}
#MessagingGateway()
public interface ToTCP {
#Gateway(requestChannel = "toTcp.input", replyChannel = "outputChannel")
public byte[] sends(byte[] data, #Header("host") String host, #Header("port") int port);
}
#Bean
public IntegrationFlow toTcp() {
return f -> f.route(new ClientTcpRouter());
}
// I am not sure I understand IntegrationFlowContext,but it works
public static class ClientTcpRouter extends AbstractMessageRouter {
#Autowired
private IntegrationFlowContext flowContext;
#Override
protected synchronized Collection<MessageChannel> determineTargetChannels(Message<?> message) {
// connection to server side.
TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port); //?? this connection factory does's closed when inGate's connection factory throw SoftEndOfStreamException
TcpOutboundGateway handler = new TcpOutboundGateway();
handler.setConnectionFactory(cf);
cf.setDeserializer(new DynamicSerSeri());
cf.setSerializer(new DynamicSerSeri());
IntegrationFlow flow = f -> f.handle(handler);
IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
this.flowContext.registration(flow)
.addBean(cf)
.id(hostPort + ".flow")
.register();
MessageChannel inputChannel = flowRegistration.getInputChannel();
this.subFlows.put(hostPort, inputChannel);
return inputChannel;
}
}
TcpInboundGateway get the connection from client into the inputChannel, and I use a serviceActivator to get inputChannel's payload and send to server side by a TcpOutboundGateway which has a connection factory with server side.
when the client closed the connection with spring-integration-ip-module, TcpInboundGateway can get the exception in SoftEndOfStreamException, but I don't known how to closed TcpOutboundGateway's connection to the server side.
Use an ApplicationListener bean or #EventListener method to listen for TCP Events.
When you first open an outbound connection, you will get a TcpConnectionOpenEvent. It is published on (and will be receive on) the calling thread by default. You can associate the outbound connection id with the inbound.
Listen for TcpConnectionCloseEvent from the inbound connection factory; you can then close the outbound connection using its connectionId.
outboundFactory.closeConnection(connectionId);
EDIT
Since you are using a TcpNetServerConnectionFactory, you can use a ThreadAffinityClientConnectionFactory which will automatically associate the outgoing connection with the incoming connection.
When you get the event for the incoming connection close, it will be on the same thread, so you can simply call releaseConnection() on that thread and the outgoing connection will close.
Here is an example
#SpringBootApplication
public class So55207274Application {
public static void main(String[] args) {
SpringApplication.run(So55207274Application.class, args);
}
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(Tcp.inboundGateway(server()))
.log()
.handle(Tcp.outboundGateway(threadBoundClient()))
.get();
}
#Bean
public TcpNetServerConnectionFactory server() {
return new TcpNetServerConnectionFactory(1234);
}
#Bean
public ThreadAffinityClientConnectionFactory threadBoundClient() {
return new ThreadAffinityClientConnectionFactory(client());
}
public TcpNetClientConnectionFactory client() {
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", 1235);
client.setSingleUse(true);
return client;
}
#EventListener
public void listen(TcpConnectionCloseEvent event) {
if (event.getConnectionFactoryName().equals("server")) {
try {
threadBoundClient().releaseConnection();
}
catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(event);
}
// Test server
#Bean
public IntegrationFlow test() {
return IntegrationFlows.from(Tcp.inboundGateway(Tcp.netServer(1235)))
.transform(Transformers.objectToString())
.<String, String>transform(p -> p.toUpperCase())
.get();
}
}

ServiceActivator does not receive message from ImapIdleChannelAdapter

ServiceActivator does not receive messages from ImapIdleChannelAdapter...
JavaMail logs successful FETCH, but MIME messages do not get delivered to SA endpoint... I want to understand what is wrong in my code.
A7 FETCH 1:35 (ENVELOPE INTERNALDATE RFC822.SIZE FLAGS BODYSTRUCTURE)
* 1 FETCH (ENVELOPE ("Fri....
Code snippet below:
`
#Autowired
EmailConfig emailCfg;
#Bean
public SubscribableChannel mailChannel() {
return MessageChannels.direct().get();
}
#Bean
public ImapIdleChannelAdapter getMailAdapter() {
ImapMailReceiver mailReceiver = new ImapMailReceiver(emailCfg.getImapUrl());
mailReceiver.setJavaMailProperties(javaMailProperties());
mailReceiver.setShouldDeleteMessages(false);
mailReceiver.setShouldMarkMessagesAsRead(true);
ImapIdleChannelAdapter imapIdleChannelAdapter = new ImapIdleChannelAdapter(mailReceiver);
imapIdleChannelAdapter.setOutputChannel(mailChannel());
imapIdleChannelAdapter.setAutoStartup(true);
imapIdleChannelAdapter.afterPropertiesSet();
return imapIdleChannelAdapter;
}
#ServiceActivator(inputChannel = "mailChannel")
public void receive(String mail) {
log.warn(mail);
}
private Properties javaMailProperties() {
Properties javaMailProperties = new Properties();
javaMailProperties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
javaMailProperties.setProperty("mail.imap.socketFactory.fallback", "false");
javaMailProperties.setProperty("mail.store.protocol", "imaps");
javaMailProperties.setProperty("mail.debug", "true");
javaMailProperties.setProperty("mail.imap.ssl", "true");
return javaMailProperties;
}
`
The problem was due to wrong bean initialization. Full version that works OK:
#Slf4j
#Configuration
#EnableIntegration
public class MyMailAdapter {
#Autowired
EmailConfig emailCfg;
#Bean
public SubscribableChannel mailChannel() {
log.info("Channel ready");
return MessageChannels.direct().get();
}
#Bean
public ImapMailReceiver receiver() {
ImapMailReceiver mailReceiver = new ImapMailReceiver(emailCfg.getImapUrl());
mailReceiver.setJavaMailProperties(javaMailProperties());
mailReceiver.setShouldDeleteMessages(false);
mailReceiver.setShouldMarkMessagesAsRead(true);
return mailReceiver;
}
#Bean
public ImapIdleChannelAdapter adapter() {
ImapIdleChannelAdapter imapIdleChannelAdapter = new ImapIdleChannelAdapter(receiver());
imapIdleChannelAdapter.setOutputChannel(mailChannel());
imapIdleChannelAdapter.afterPropertiesSet();
return imapIdleChannelAdapter;
}
#ServiceActivator(inputChannel = "mailChannel")
public void receive(Message<MimeMessage> mail) throws MessagingException {
log.info(mail.getPayload().toString());
}
private Properties javaMailProperties() {
Properties javaMailProperties = new Properties();
javaMailProperties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
javaMailProperties.setProperty("mail.imap.socketFactory.fallback", "false");
javaMailProperties.setProperty("mail.store.protocol", "imaps");
javaMailProperties.setProperty("mail.debug", "true");
javaMailProperties.setProperty("mail.imap.ssl", "true");
return javaMailProperties;
}
}
I don't know what's exactly wrong with your code but I will suggest you few approches that could help you.
Firstly I suggest you to use java DSL in java based configuration. It will provide you nice way to directly specific flow of your integration application (and avoid simply mistakes). For example for spliiter and service activator:
#Bean
public IntegrationFlow yourFlow(AbstractMessageSplitter splitter,
MessageHandler handler) {
return
IntegrationFlows
.from(CHANNEL)
.split(splitter)
.handle(handler).get();
}
Secondly it's generally bad idea to directly specify message type to String. Try something like this (why String?):
#ServiceActivator(inputChannel = "mailChannel")
public void receive(Message<?> message) {
/* (String) message.getPayload() */
}
Maybe it's not a case but let's check it.

Dummy backend for ISO 8583 message

I want to create dummy backend for ISO 8583 messages. The necessary condition for this problem is "to run the jPos/other ATM test-suite against this backend".
You may try simple jReactive-iso8583 server.
Have a look at client-server integration test.
Usage example:
Spring config:
#Configuration
public class Iso8583ServerConfig {
#Value("${iso8583.connection.port}")
int port;
#Bean
public org.jreactive.iso8583.server.Iso8583Server iso8583Server() throws IOException {
return new org.jreactive.iso8583.server.Iso8583Server(port, serverMessageFactory());
}
#Bean
MessageFactory serverMessageFactory() throws IOException {
MessageFactory messageFactory = ConfigParser.createDefault();
messageFactory.setCharacterEncoding(StandardCharsets.US_ASCII.name());
messageFactory.setUseBinaryMessages(false);
messageFactory.setAssignDate(true);
return messageFactory;
}
}
Add custom message listener somewhere:
server.addMessageListener(new IsoMessageListener() {
#Override
public boolean applies(IsoMessage isoMessage) {
return isoMessage.getType() == 0x200;
}
#Override
public boolean onMessage(ChannelHandlerContext ctx, IsoMessage isoMessage) {
capturedRequest = isoMessage;
final IsoMessage response = server.getIsoMessageFactory().createResponse(isoMessage);
response.setField(39, IsoType.ALPHA.value("00", 2));
response.setField(60, IsoType.LLLVAR.value("XXX", 3));
ctx.writeAndFlush(response);
return false;
}
});
Then run a server:
server.init();
server.start();
Shutdown server:
server.shutdown();

Resources