Spring-boot 2 rabbitmq MessageConverter not working as excepted - spring-rabbit

I am using the springboot and rabitmq. I have kind of similar scenario here.
I would like to map my message to custom java object
I also want pass delivery tag which is message proprieties
I wann to pass Channel as well beacuse I need to manual ack messages
My code is like this
#RunWith(SpringRunner.class)
public class EPPQ2SubscriberTest {
#Autowired
private RabbitListenerEndpointRegistry registry;
public final String sampleMessage = "{" + "\"header\": {" + "\"RETRY_COUNT\":0," + "\"PUBLISH_EVENT_TYPE\":\"AUTH\""
+ "}," + "\"payLoad\":{" + "\"MTI\": \"120\"," + "\"MTI_REQUEST\": \"120\","
+ "\"PAN\": \"6011000000000000\"" + "}" + "}";
#Test
public void message_converter_test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("messageListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody(sampleMessage.getBytes())
.andProperties(MessagePropertiesBuilder.newInstance().setContentType("application/json").build())
.build();
listener.onMessage(message, mock(Channel.class));
}
#Configuration
#EnableRabbit
public static class config {
#Bean
public ConnectionFactory mockConnectionFactory() {
return mock(ConnectionFactory.class);
}
#Bean
public MessageConverter messageConverter() {
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
/*
* DefaultClassMapper classMapper = new DefaultClassMapper();
* classMapper.setDefaultType(com.discover.dftp.scrubber.domain.Message.class);
* messageConverter.setClassMapper(classMapper);
*/
return messageConverter;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(mockConnectionFactory());
factory.setMessageConverter(messageConverter());
factory.setAutoStartup(false);
return factory;
}
#Bean
public EPPQ2Subscriber messageListener() {
return new EPPQ2Subscriber();
}
}
}
#Component
public class EPPQ2Subscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(EPPQ2Subscriber.class);
// #RabbitListener(queues = "#{queue.getName()}") #TODO I wann to use this in
// later point in time.. !
#RabbitListener(id = "messageListener", queues = "TestQueue")
public void receiveMessage(Message message, Channel channel/* ,#Header(AmqpHeaders.DELIVERY_TAG) long tag */) {
LOGGER.info("Method receiveMessage invoked");
message.getMessageProperties().getDeliveryTag();
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
}
}
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Object> header;
private Map<String, Object> payLoad;
public Map<String, Object> getHeader() {
return header;
}
public void setHeader(Map<String, Object> header) {
this.header = header;
}
public Map<String, Object> getPayLoad() {
return payLoad;
}
public void setPayLoad(Map<String, Object> payLoad) {
this.payLoad = payLoad;
}
#Override
public String toString() {
return "Header [header=" + this.header + ", payLoad=" + this.payLoad + "]";
}
}

#RabbitListener(id = "messageListener", queues = "TestQueue")
public void receiveMessage(Message message, Channel channel/* ,#Header(AmqpHeaders.DELIVERY_TAG) long tag */) {
LOGGER.info("Method receiveMessage invoked");
message.getMessageProperties().getDeliveryTag();
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
}
I looks like that method receives the raw (unconverted) Spring AMQP Message (you are importing the wrong Message class in EPPQ2Subscriber).

Related

Listener not getting message in REDIS PubS/ub with Spring Boot

I am relatively new to Redis Pub/Sub. I have integrated this recently in my Spring Boot application.
Redis Pub/Sub configuration is as follows:
#Configuration
public class RedisPubSubConfiguration {
#Bean
public RedisMessageListenerContainer messageListenerContainer(RedisConnectionFactory
connectionFactory,
#Qualifier("topicAdapterPair")
List<Pair
<Topic,
MessageListenerAdapter>>
channelAdaperPairList) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
for (Pair<Topic, MessageListenerAdapter> chanelAdapterPair : channelAdaperPairList) {
container.addMessageListener(chanelAdapterPair.getValue(),
chanelAdapterPair.getKey());
}
container.setConnectionFactory(connectionFactory);
return container;
}
#Bean("msg-listener-adptr-1")
public MessageListenerAdapter messageListnerAdapter1(
#Qualifier("message-listener-1")
MessageListener listener) {
return new MessageListenerAdapter(listener, REDIS_RECEIVER_METHOD_NAME);
}
#Bean("message-listener-1")
public MessageListener messageListener1(ManagerProxy managerProxy) {
return new MessageListener1(managerProxy);
}
#Bean("message-sender-1")
public MessageSender messageSender1(RedisTemplate redisTemplate,
#Value("${chnlTopicName1}")
String channelTopicName) {
return new MessageSender1(redisTemplate, channelTopicName);
}
#Bean
#Qualifier("topicAdapterPair")
public Pair<Topic, MessageListenerAdapter> getTopicListenerAdapterpair1(
#Value("${chnlTopicName1}") String channelTopicName,
#Qualifier("msg-listener-adptr-1")
MessageListenerAdapter messageListenerAdapter) {
return Pair.of(new ChannelTopic(channelTopicName), messageListenerAdapter);
}
#Bean("msg-listener-adptr-2")
public MessageListenerAdapter messageListnerAdapter2(
#Qualifier("message-listener-2")
MessageListener listener) {
return new MessageListenerAdapter(listener, REDIS_RECEIVER_METHOD_NAME);
}
#Bean("message-listener-2")
public MessageListener messageListener2(NotificationServiceImpl notificationService) {
return new MessageListener2(notificationService);
}
#Bean("message-sender-2")
public MessageSender messageSender2(RedisTemplate redisTemplate,
#Value("${chnlTopicName2}")
String channelTopicName) {
return new MessageSender2(redisTemplate, channelTopicName);
}
#Bean
#Qualifier("topicAdapterPair")
public Pair<Topic, MessageListenerAdapter> getTopicListenerAdapterPair2(
#Value("${chnlTopicName2}") String channelTopicName,
#Qualifier("msg-listener-adptr-2")
MessageListenerAdapter messageListenerAdapter) {
return Pair.of(new ChannelTopic(channelTopicName), messageListenerAdapter);
}
}
MessageSender2 is as follows:
public class MessageSender2 implements MessageSender<MyDTO> {
private final RedisTemplate<String, Object> redisTemplate;
private final String chanelName;
public MessageSender2(
RedisTemplate<String, Object> redisTemplate,
String chanelName) {
this.redisTemplate = redisTemplate;
this.chanelName = chanelName;
}
#Override
public void send(MyDTO myDTO) {
redisTemplate.convertAndSend(chanelName, myDTO);
}
}
MessageListener2 is as follows:
public class MessageListener2 implements MessageListener<EventDTO> {
private static final Logger LOGGER = LoggerFactory
.getLogger(MessageListener2.class);
private final NotificationService notificationService;
public MessageListener1(NotificationServiceImpl notificationService) {
this.notificationService = notificationService;
}
#Override
public void receiveMessage(MyDTO message) {
LOGGER.info("Received message : {} ", message); <--HERE MESSAGE IS NOT COMING EVEN AFTER PUBLISHING MESSAGE TO THE ASSOCIATED TOPIC FROM PUBLISHER
Type type = message.getType();
...
}
}
MessageSender1 is as follows:
public class MessageSender1 implements MessageSender<String> {
private final RedisTemplate<String, Object> redisTemplate;
private final String chanelName;
public MessageSender1(
RedisTemplate<String, Object> redisTemplate,
String chanelName) {
this.redisTemplate = redisTemplate;
this.chanelName = chanelName;
}
#Override
public void send(String message) {
redisTemplate.convertAndSend(chanelName, message);
}
}
Associated listener is follows:
public class MessageListener1 implements MessageListener<String> {
private static final Logger LOGGER = LoggerFactory
.getLogger(MessageListener1.class);
private final ManagerProxy managerProxy;
public MessageListener1(ManagerProxy managerProxy) {
this.managerProxy = managerProxy;
}
public void receiveMessage(String message) {
LOGGER.info("Received message : {} ", message);
managerProxy.refresh();
}
}
Here though MessageSender1 and associated message listener are working fine, I don't understand what I did with MessageSender2 and associated listener, because of which I am not able to receive message in the listener.

Spring AMQP RabbitMQ Object sent as one type gets converted to Map in Listener

In my application, the RabbitTemplate sends an object (EventMessage - code below) to the queue. However in the RabbitListener and RabbitHandler the EmailMessage object that EventMessage contains gets converted as LinkedHashmap during deserialization
However the MessageProperties shows that the type is EventMessage
2020-09-23 18:39:47.712 WARN 16676 --- [ntContainer#0-1] o.s.a.r.r.RejectAndDontRequeueRecoverer : Retries exhausted for message (Body:'{"type":101,"params":{"emailMessage":{"hasTo":true,"hasCc":false,"hasBcc":false,"template":null,"templateParams":null,"html":false,"toAddresses":["test#test.com"],"ccAddresses":null,"bccAddresses":null,"fromAddress":"test#mhserver.com","subject":"Test Subject","message":"An email has been sent to your registered email to reset your password","isHtml":false}}}' MessageProperties [headers={__TypeId__=in.teamnexus.mq.EventMessage}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=eventExchange, receivedRoutingKey=route.key.email, deliveryTag=1, consumerTag=amq.ctag-eCfxhUrcjiCF4lA7x8ZLNg, consumerQueue=queue.email])
Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class in.teamnexus.email.EmailMessage (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; in.teamnexus.email.EmailMessage is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader #ac49aa4)
at in.teamnexus.mq.EmailQueueListener.handleEvent(EmailQueueListener.java:34) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
Following is the RabbitConfig
#Configuration
#EnableRabbit
#PropertySource("classpath:custom.properties")
public class RabbitConfig
{
#Bean
public ConnectionFactory rabbitConnectionFactory()
{
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(rabbitMQhost);
connectionFactory.setUsername(rabbitMQUsername);
connectionFactory.setPassword(rabbitMQPassword);
connectionFactory.setVirtualHost(rabbitMQVirtualHost);
connectionFactory.setPort(rabbitMQPort);
return connectionFactory;
}
#Bean
public MessageConverter messageConverter()
{
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(new ObjectMapper());
return messageConverter;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory()
{
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setMessageConverter(messageConverter());
factory.setConnectionFactory(rabbitConnectionFactory());
factory.setConcurrentConsumers(20);
factory.setPrefetchCount(1);
factory.setMaxConcurrentConsumers(100);
factory.setAdviceChain(retryInterceptor());
return factory;
}
#Bean
public RetryOperationsInterceptor retryInterceptor()
{
return RetryInterceptorBuilder.stateless().maxAttempts(5).backOffOptions(1000, 2.0, 10000)
.recoverer(new RejectAndDontRequeueRecoverer()).build();
}
#Bean
public Queue mailQueue()
{
return new Queue("queue.email", true);
}
#Bean
public RabbitTemplate rabbitTemplate()
{
RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
rabbitTemplate.setMessageConverter(messageConverter());
return rabbitTemplate;
}
#Bean
public AmqpAdmin amqpAdmin()
{
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory());
return rabbitAdmin;
}
#Bean
public Exchange exchange()
{
AmqpAdmin rabbitAdmin = amqpAdmin();
DirectExchange dirExchange = new DirectExchange("eventExchange", true, false);
rabbitAdmin.declareExchange(dirExchange);
rabbitAdmin.declareQueue(mailQueue());
Binding emailBinding = BindingBuilder.bind(mailQueue()).to(dirExchange).with(Constants.ROUTE_KEY_EMAIL);
rabbitAdmin.declareBinding(emailBinding);
rabbitAdmin.declareBinding(retryBinding);
return dirExchange;
}
#Bean
Publisher publisher()
{
PublisherImpl publisher = new PublisherImpl();
return publisher;
}
#Bean
EmailQueueListener emailQueueListener()
{
return new EmailQueueListener();
}
}
The EventMessage class
public class EventMessage<T> implements Serializable
{
public static final int TYPE_EMAIL = 101;
/**
*
*/
private static final long serialVersionUID = 1846120191276045453L;
#JsonProperty("type")
private int type;
#JsonProperty("params")
private Map<String, T> params;
public EventMessage()
{
}
public EventMessage(int type, Map<String, T> params)
{
this.type = type;
this.params = params;
}
// Getters Setters...
}
The PublisherImpl class
public class PublisherImpl implements Publisher
{
private Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public <T> EventResponse publishMessage(EventMessage<T> message, boolean async)
{
EventResponse response = new EventResponse();
if (async)
{
int resp = doPublish(message);
if (resp == EventResponse.EVENT_SUCCESS)
{
response.setStatus(EventResponse.EVENT_SUCCESS);
response.setMessage("Event Successfully published to the queue");
}
else
{
response.setStatus(EventResponse.EVENT_FAILURE);
response.setMessage("Failed to publish Event to the queue");
}
}
else
{
doSyncOp(message)
}
return response;
}
private <T> int doPublish(EventMessage<T> message)
{
String routingKey = Constants.ROUTE_KEY_EVENT;
int retVal = EventResponse.EVENT_SUCCESS;
try
{
switch (message.getType())
{
case EventMessage.TYPE_EMAIL:
{
routingKey = Constants.ROUTE_KEY_EMAIL;
this.rabbitTemplate.convertAndSend("eventExchange", routingKey, message);
break;
}
}
}
catch (AmqpException e)
{
retVal = EventResponse.EVENT_FAILURE;
logger.debug("Unable to push to the queue", e);
}
return retVal;
}
// Getters/Setters
}
The EmailQueueListener class
#RabbitListener(containerFactory = "rabbitListenerContainerFactory", queues = "queue.email")
public class EmailQueueListener
{
Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private EmailSender emailSender;
#RabbitHandler
void handleEvent(EventMessage<EmailMessage> message)
{
logger.debug("### RabbitHandler Email: Receiving in listener:" + message);
Map<String, EmailMessage> params = message.getParams();
logger.debug("### emailMessage: " + params.get("emailMessage") + " class:" + params.get("emailMessage").getClass());
EmailMessage email = (EmailMessage) params.get("emailMessage");
emailSender.sendEmail(email);
}
// Getters/Setters
}
At the line where params.get("emailMessage") is called is where I get the exception and the configured amount of retries. I am not sure if I am doing something wrong.
EDIT
Here is the code that publishes the message
public class EmailHelper
{
#Autowired
private Publisher publisher;
public void sendEmail(String to, String cc, String bcc, String subject, String text, boolean isHtml)
{
EmailMessage emailMessage = new EmailMessage();
emailMessage.setToAddresses(new String[] { to });
if (cc != null && !cc.isEmpty())
{
emailMessage.setCcAddresses(new String[] { cc });
}
if (bcc != null && !bcc.isEmpty())
{
emailMessage.setBccAddresses(new String[] { bcc });
}
emailMessage.setFromAddress("test#mhserver.com");
emailMessage.setSubject(subject);
emailMessage.setMessage(text);
Map<String, EmailMessage> params = new HashMap<>();
params.put("emailMessage", emailMessage);
EventMessage<EmailMessage> evtMsg = new EventMessage<>(EventMessage.TYPE_EMAIL, params);
publisher.<EmailMessage>publishMessage(evtMsg, true);
}
It's because EventMessage is a generic type; the default type mapper in the message converter can't handle arbitrary generic types.
It will work if the #RabbitListener is defined at the method level instead of the class level because we can infer the generic type from the listener method parameter.
Otherwise, you will need to create a custom type mapper for the message converter.

How send reply from listener

I am working on homework to my studies with Java and RabbitMQ. I am not familiar much with Spring and RabbitMQ, but I can't handle this problem.
I have 2 single application.
First one, which produces the message (bolid application)
I created a producer of the message (bolid), which every 10 seconds send a message to listeners
#SpringBootApplication
public class BolidApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(BolidApplication.class, args);
}
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public void run(String... args) throws Exception {
Bolid bolid = new Bolid();
int i = 10;
while (true) {
bolid.setData(new Date());
rabbitTemplate.setReplyAddress("bolidReply");
rabbitTemplate.convertAndSend("RaceExchange", "raceRouting", bolid.toString());
rabbitTemplate.convertAndSend("MonitorExchange", "raceRouting", bolid.toString());
Thread.sleep(15000);
i += 10;
}
}
}
So, I create 2 queue (RaceQueue and MonitorQueue), define exchange and bind them.
I have 2 listeners: RaceListener and MonitorListener.
There is the code of my listeners:
And the second application, which is listeners.
public class RabbitConfig {
private static final String RACE_QUEUE = "RaceQueue";
private static final String MONITOR_QUEUE = "MonitorQueue";
#Bean
Queue myQueue() {
return new Queue(RACE_QUEUE, true);
}
#Bean
Queue monitorQueue() {
return new Queue(MONITOR_QUEUE, true);
}
#Bean
Exchange myExchange() {
return ExchangeBuilder.topicExchange("RaceExchange")
.durable(true)
.build();
}
#Bean
Exchange monitorExchange() {
return ExchangeBuilder.topicExchange("MonitorExchange")
.durable(true)
.build();
}
#Bean
Binding binding() {
// return new Binding(MY_QUEUE, Binding.DestinationType.QUEUE, "MyTopicExchange", "topic", null)
return BindingBuilder
.bind(myQueue())
.to(myExchange())
.with("raceRouting")
.noargs();
}
#Bean
Binding monitorBinding() {
return BindingBuilder
.bind(monitorQueue())
.to(monitorExchange())
.with("raceRouting")
.noargs();
}
#Bean
ConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory("localhost");
cachingConnectionFactory.setUsername("guest");
cachingConnectionFactory.setPassword("guest");
return cachingConnectionFactory;
}
#Bean
MessageListenerContainer rabbitRaceListener() {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
simpleMessageListenerContainer.setConnectionFactory(connectionFactory());
simpleMessageListenerContainer.setQueues(myQueue());
simpleMessageListenerContainer.setupMessageListener(new RabbitRaceListener());
return simpleMessageListenerContainer;
}
#Bean
MessageListenerContainer rabbitMonitorListener() {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
simpleMessageListenerContainer.setConnectionFactory(connectionFactory());
simpleMessageListenerContainer.setQueues(monitorQueue());
simpleMessageListenerContainer.setupMessageListener(new RabbitMonitorListener());
return simpleMessageListenerContainer;
}
}
From MonitorListener I want to use reply pattern to reply message to my first application (bolid application). So Bolid application can receive my message.
My Code for MonitorListener:
public class RabbitMonitorListener implements MessageListener {
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public void onMessage(Message message) {
String[] splitted = new String(message.getBody()).split("\\|");
int oilTemperature = Integer.parseInt(splitted[1].split(" ")[2]);
int engineTemperature = Integer.parseInt(splitted[2].split(" ")[2]);
int tirePressure = Integer.parseInt(splitted[3].split(" ")[2]);
System.out.println("message2 = [" + new String(message.getBody()) + "]");
if (oilTemperature > 120 || engineTemperature > 120 || tirePressure > 12) {
System.out.println("SEND REPLY TO BOLID!");
}
if (oilTemperature > 150 || engineTemperature > 150 || tirePressure > 17) {
System.out.println("SEND RELY TO BOLID!");
}
}
}
How can I achieve that? So here I can send the message go back to bolid and on the bolid application I can read it?
EDIT:
I did some research, I want to do it like this way:
public class RabbitMonitorListener implements MessageListener {
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public void onMessage(Message message) {
String[] splitted = new String(message.getBody()).split("\\|");
int oilTemperature = Integer.parseInt(splitted[1].split(" ")[2]);
int engineTemperature = Integer.parseInt(splitted[2].split(" ")[2]);
int tirePressure = Integer.parseInt(splitted[3].split(" ")[2]);
String response = "Hello";
MessageProperties properties = new MessageProperties();
Message responseMessage = new Message(response.getBytes(), properties);
rabbitTemplate.send(message.getMessageProperties().getReplyTo(), responseMessage);
System.out.println("message2 = [" + new String(message.getBody()) + "]");
if (oilTemperature > 120 || engineTemperature > 120 || tirePressure > 12) {
System.out.println("WARN MECHANICS");
}
if (oilTemperature > 150 || engineTemperature > 150 || tirePressure > 17) {
System.out.println("WARN MECHANICS");
}
}
}
but the rabbitTemplate is null here, so I can't #Autowired it here. How can I have access to rabbitTemplate and method send in MessageListener?
new RabbitRaceListener() - that must be a #Bean too, to get auto wiring.
However, you are over-complicating things; the framework can take care of all of this for you.
See Request/Reply Messaging for the client side - and use convertSendAndReceive() or convertSendAndReceiveAsType().
On the server side, see Annotation-driven Listener Endpoints.
#RabbitListener(queues = "request")
public String handle(String in) {
return in.toUpperCase();
}

Convert to multiple client server Connection

making my previous question more readable, following is my code which works fine for single server single client connection, but i want my client to connect 2 or more servers dynamically,
public class ClientCall {
public static void main(String[] args) {
#SuppressWarnings("resource")
ApplicationContext ctx = new AnnotationConfigApplicationContext(GatewayConfig.class);
GatewayService gatewayService = ctx.getBean(GatewayService.class);
//int i=0;
Message message = new Message();
/*while(i<4)
{*/
message.setPayload("It's working");
gatewayService.sendMessage(message);
/* i++;
}*/
}
}
public class Message {
private String payload;
// getter setter
}
#EnableIntegration
#IntegrationComponentScan
#Configuration
#ComponentScan(basePackages = "com.gateway.service")
public class GatewayConfig {
// #Value("${listen.port:6788}")
private int port = 6785;
#Autowired
private GatewayService<Message> gatewayService;
#MessagingGateway(defaultRequestChannel = "sendMessageChannel")
public interface Gateway {
void viaTcp(String payload);
}
#Bean
public AbstractClientConnectionFactory clientCF() {
TcpNetClientConnectionFactory clientConnectionFactory = new TcpNetClientConnectionFactory("localhost",
this.port);
clientConnectionFactory.setSingleUse(false);
return clientConnectionFactory;
}
#Bean
#ServiceActivator(inputChannel = "sendMessageChannel")
public MessageHandler tcpOutGateway(AbstractClientConnectionFactory connectionFactory) {
TcpOutboundGateway outGateway = new TcpOutboundGateway();
outGateway.setConnectionFactory(connectionFactory);
// outGateway.setAsync(true);
outGateway.setOutputChannel(receiveMessageChannel());
outGateway.setRequiresReply(true);
outGateway.setReplyChannel(receiveMessageChannel());
return outGateway;
}
#Bean
public MessageChannel sendMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Bean
public MessageChannel receiveMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Transformer(inputChannel = "receiveMessageChannel", outputChannel = "processMessageChannel")
public String convert(byte[] bytes) {
return new String(bytes);
}
#ServiceActivator(inputChannel = "processMessageChannel")
public void upCase(String response) {
gatewayService.receiveMessage(response);
}
#Transformer(inputChannel = "errorChannel", outputChannel = "processMessageChannel")
public void convertError(byte[] bytes) {
String str = new String(bytes);
System.out.println("Error: " + str);
}
}
public interface GatewayService<T> {
public void sendMessage(final T payload);
public void receiveMessage(String response);
}
#Service
public class GatewayServiceImpl implements GatewayService<Message> {
#Autowired
private Gateway gateway;
#Autowired
private GatewayContextManger<String, Object> gatewayContextManger;
#Override
public void sendMessage(final Message message) {
new Thread(new Runnable() {
#Override
public void run() {
gateway.viaTcp(message.getPayload());
}
}).start();
}
#Override
public void receiveMessage(final String response) {
new Thread(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.setPayload(response);
Object obj = gatewayContextManger.get(message.getPayload());
synchronized (obj) {
obj.notify();
}
}
}).start();
}
}
and below is sever code similar there is another server with different port and ip then how to make connections to these servers?
class TCPServer
{
public static void main(String argv[]) throws Exception
{
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6785);
while(true)
{
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient =
new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
clientSentence = inFromClient.readLine();
System.out.println("Received: " + clientSentence);
capitalizedSentence = clientSentence + "\r\n";
outToClient.writeBytes(capitalizedSentence);
}
}
}
A few comments.
Instead of starting a thread to send the message, simply make sendMessageChannel an ExecutorChannel, using a ThreadPoolTaskExecutor - it will be more efficient and gets you out of the business of managing threads.
If you only have 2 servers to connect to, rather than coming up with a dynamic scheme, simply define 2 TCP adapters and add a #Router after sendMessageChannel.
You can tell the router which server to send it to by setting a header.
#MessagingGateway(defaultRequestChannel = "sendMessageChannel")
public interface Gateway {
void viaTcp(String payload #Header("which") String target);
}
Use a HeaderValueRouter to route on header which.
See Message Routing in the reference manual.

How to create and hold multiple connections in Spring Integration

I have one server and number of clients, server will send response and waits for acknowledgement, additionally I want to hold that connection forever for next message and acknowledgement how should i create these connection in Spring Integration. I read about Spring integration, i couldn't find out the solution for holding the connection.
public class ClientCall {
public static void main(String[] args) {
#SuppressWarnings("resource")
ApplicationContext ctx = new AnnotationConfigApplicationContext(GatewayConfig.class);
GatewayService gatewayService = ctx.getBean(GatewayService.class);
//int i=0;
Message message = new Message();
/*while(i<4)
{*/
message.setPayload("It's working");
gatewayService.sendMessage(message);
/* i++;
}*/
}
}
#Service
public interface GatewayService<T> {
public void sendMessage(final T payload);
public void receiveMessage(String response);
}
#EnableIntegration
#IntegrationComponentScan
#Configuration
#ComponentScan(basePackages = "com.gateway.service")
public class GatewayConfig {
// #Value("${listen.port:6788}")
private int port = 6785;
#Autowired
private GatewayService<Message> gatewayService;
#MessagingGateway(defaultRequestChannel = "sendMessageChannel")
public interface Gateway {
void viaTcp(String payload);
}
#Bean
public AbstractClientConnectionFactory clientCF() {
TcpNetClientConnectionFactory clientConnectionFactory = new TcpNetClientConnectionFactory("localhost",this.port);
clientConnectionFactory.setSingleUse(true);
return clientConnectionFactory;
}
#Bean
#ServiceActivator(inputChannel = "sendMessageChannel")
public MessageHandler tcpOutGateway(AbstractClientConnectionFactory connectionFactory) {
TcpOutboundGateway outGateway = new TcpOutboundGateway();
outGateway.setConnectionFactory(connectionFactory);
outGateway.setAsync(true);
outGateway.setOutputChannel(receiveMessageChannel());
outGateway.setRequiresReply(true);
outGateway.setReplyChannel(receiveMessageChannel());
return outGateway;
}
#Bean
public MessageChannel sendMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Bean
public MessageChannel receiveMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Transformer(inputChannel = "receiveMessageChannel", outputChannel = "processMessageChannel")
public String convert(byte[] bytes) {
return new String(bytes);
}
#ServiceActivator(inputChannel = "processMessageChannel")
public void upCase(String response) {
gatewayService.receiveMessage(response);
}
#Transformer(inputChannel = "errorChannel", outputChannel = "processMessageChannel")
public void convertError(byte[] bytes) {
String str = new String(bytes);
System.out.println("Error: " + str);
}
}
public class Message {
private String payload;
// getter setter
}
#Service
public class GatewayServiceImpl implements GatewayService<Message> {
#Autowired
private Gateway gateway;
#Autowired
private GatewayContextManger<String, Object> gatewayContextManger;
#Override
public void sendMessage(final Message message) {
new Thread(new Runnable() {
#Override
public void run() {
gateway.viaTcp(message.getPayload());
}
}).start();
}
#Override
public void receiveMessage(final String response) {
new Thread(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.setPayload(response);
Object obj = message;
//Object obj = gatewayContextManger.get(message.getPayload());
synchronized (message) {
obj.notify();
System.out.println("Message Received : "+message.getPayload());
}
}
}).start();
}
}
You have: clientConnectionFactory.setSingleUse(true); This means the connection will be closed after the request; leave it false (default) to keep the connection open.

Resources