Working fine with Spring JMS and getting problem with Apache camel route , IBM MQ Route - spring

I'm Sending request to the Third party IBM MQ with space pattern, like below text (COBOL input file), but i'm getting reponse as a error in Reply queue like below but same input working fine spring jms, i'm facing this problem with apache camel
Sample Input:
GetProducerWithCommissionddRates
C26115
77104 99998
2010-01-01 011
Response:
printing data in controller:: EPPRD02007EAn invalid COBOL Date Format
was provided as input to the Producer Retrieval Service, Get Producer
With Commission Rates Operation: AsOfDate. Source:
(ProducerMediationModule)
ProducerMediationModule
Controller with camel:
#PostMapping("/request-reply")
public String requestReplyMapping(#RequestBody String inputReq) {
Exchange exchangeRequest = ExchangeBuilder.anExchange(camelContext).withBody(inputReq).build();
Exchange exchangeResponse = producer.send("direct:request-reply", exchangeRequest);
return exchangeResponse.getIn().getBody(String.class);
}
Camel Route:
#Component
public class RequestReplyRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:request-reply").
to("jms:REQUEST.Q1?ReplyTo=REPLY.Q1&exchangePattern=InOut")
.log("Request-reply Body is ${body} and header info is ${headers} ");
from("jms:REPLY.Q1")
.log("Received Body is ${body} and header info is ${headers} ");
}
}
JMS Controller Code(working) :
#PostMapping(value="request-reply")
public ResponseEntity<String> sendRequestAndReceiveReply (#RequestBody String prd2x4Request) { System.out.println("received prd2x4 request: "+prd2x4Request);
String reply = prd2x4Wrapper.sendReqAndGetMessageId(prd2x4Request);
return new ResponseEntity<String>(reply, HttpStatus.OK);
}
JMS MQ Calling (working) :
private String sendReqAndGetMessage(String prd2x4Request) {
String messageId = "";
MQQueue requestQueue = new MQQueue(RequestQueue);
Session session = jmsTemplate.getConnectionFactory().createConnection().createSession(); MessageProducer mp = session.createProducer(requestQueue);
TextMessage message = session.createTextMessage(prd2x4Request);
message.setStringProperty("JMS_IBM_Character_Set", "IBM037");
mp.send(message);
messageId = message.getJMSMessageID();
mp.close();
session.close();
return messageId;
}

Related

Spring Integration Mqtt : DestinationResolutionException: no output-channel or replyChannel header available

Please can someone help me to understand where is the probleme in this config:
Versions :
org.springframework.integration:spring-integration-mqtt:5.5.2
org.springframework.boot:spring-boot-starter:2.5.3
org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5
#Configuration
public class MqttConfig {
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[] { "tcp://localhost:1883" });
return factory;
}
#Bean
public MqttPahoMessageDrivenChannelAdapter inboundAdapter(MqttPahoClientFactory clientFactory) {
return new MqttPahoMessageDrivenChannelAdapter("MyApp", clientFactory, "ReplyTopic");
}
#Bean
IntegrationFlow inboundFlow(MqttPahoMessageDrivenChannelAdapter inboundAdapter) {
return IntegrationFlows.from(inboundAdapter)
.bridge()
.channel("replyChannel")
.get();
}
#Bean
public MessageChannel replyChannel() {
return MessageChannels.publishSubscribe().get();;
}
#Bean
public MqttPahoMessageHandler outboundAdapter(MqttPahoClientFactory clientFactory) {
return new MqttPahoMessageHandler("MyApp", clientFactory);
}
#Bean
public IntegrationFlow outboundFlow(MqttPahoMessageHandler outboundAdapter) {
return IntegrationFlows.from("requestChannel")
.handle(outboundAdapter).get()
}
#MessagingGateway
public interface MyGateway {
#Gateway(requestChannel = "requestChannel", replyChannel = "replyChannel")
String send(String request, #Header(MqttHeaders.TOPIC) String requestTopic);
}
}
Client code
#RestController
public class MyController {
#Autowired
private MyGateway myGateway;
#GetMapping("/sendRequest")
public String sendRequest() {
var response = myGateway.send("Hello", "MyTopic");
return response;
}
}
Usage:
curl http://localhost:8080/sendRequest
manual response from the mqtt broker (HiveMQ)
docker exec -it hivemq mqtt pub -t ReplyTopic -m "World" --debug
CLIENT mqttClient-MQTT_5_0-9ecded84-8416-4baa-a8f3-d593c692bc65: acknowledged PUBLISH: 'World' for PUBLISH to Topic: ReplyTopic
But I dont know why i have this message on the Spring application output
2022-10-25 18:04:33.171 ERROR 17069 --- [T Call: MyApp] .m.i.MqttPahoMessageDrivenChannelAdapter : Unhandled exception for GenericMessage [payload=World, headers={mqtt_receivedRetained=false, mqtt_id=0, mqtt_duplicate=false, id=9dbd5e14-66ed-5dc8-6cea-6d04ef19c6cc, mqtt_receivedTopic=ReplyTopic, mqtt_receivedQos=0, timestamp=1666713873170}]
org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.integration.handler.BridgeHandler#6f63903c]; nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
Please can someone explain why i have this ?
no output-channel or replyChannel header available
I think the problem you are facing is not related to your bridge() configuration.
This comes from the MessagingGatewaySupport and its replyMessageCorrelator feature which is activated by your replyChannel = "replyChannel".
The real problem that you are trying to do what is not possible with MQTT v3. There is just no headers transferring over MQTT broker to carry on a required for gateway initiator a correlation key - the TemporaryReplyChannel. See more in docs about gateway: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#gateway.
In other words: independently of the replyChannel configuration on gateway, the replyChannel header must be present in the reply message. This is the way how gateway correlates requests with replies.
You have to look into an aggregator to send the request message in parallel and to preserve the mentioned TemporaryReplyChannel header. Then when you receive a reply (inboundAdapter) you send it to this aggregator. You need to ensure some correlation key from a request and reply payload, so they can match and fulfill group for reply to be sent back to the gateway.
See more info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-routing.html#aggregator

How can i receive the response from Apache camel route to the Spring boot controller

From the route I'm sending data to endpoint direct: receive and I'm receiving same in the controller, by using consumertemplate but getting below error
[Camel (camel-1) thread #1 -
JmsConsumer[MQT.EI.PRD2X4_PRODUCER_UT.REPLY.Q1]] ERROR
o.a.c.processor.DefaultErrorHandler.log - Failed delivery for
(MessageId: ID:c3e2d840d4d8e3f14040404040404040d85c2573b4cf7342 on
ExchangeId: ID-APINP-ELPT60235-1597255599355-0-5). Exhausted after
delivery attempt: 1 caught:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: direct://recive.
Exchange[ID-APINP-ELPT60235-1597255599355-0-5]
Can anyone please give the suggestion on how can i get the response data from the route to the controller?
Controller code:
#Autowired
private CamelContext camelContext;
#Autowired
private ProducerTemplate producer;
#Autowired
ConsumerTemplate consumer;
#PostMapping("/request-reply")
public String requestReplyMapping(#RequestBody String inputReq) {
Exchange exchangeRequest = ExchangeBuilder.anExchange(camelContext).withBody(inputReq).build();
Exchange exchangeResponse = producer.send("direct:request-reply", exchangeRequest);
Exchange receive = consumer.receive("direct:receive"); //receiving data from this endpoint
return null;
}
Route Code:
#Component
public class RequestReplyRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:request-reply").
to("jms:RequestQueue?ReplyTo=ResponseQueue&exchangePattern=InOut")
.log("Request-reply Body is ${body} and header info is ${headers} ");
from("jms:ResponseQueue")
.log("Received Body is ${body} and header info is ${headers} ")
.to("direct:receive"); // sending data to the endpoint
}
}
You don't need a consumerTemplate for what you are attempting to do. Direct component by default has a exchange pattern of InOut. So you can use the exchangeResponse variable to get the exchange after your camel routes have been processed.
Camel route:
from("direct:start")
.log("${body} in stat")
.transform().simple("text")
.to("direct:nextRoute");
from("direct:nextRoute")
.log("${body} in nextRoute");
Rest Controller:
public class RestController{
final static String CAMEL_START_URI ="direct:start";
#Autowired
CamelContext camelContext;
#Autowired
#EndpointInject(uri=CAMEL_START_URI)
ProducerTemplate producer;
#PostMapping(value = "/request")
public String requestMapping(#RequestBody String inputReq) {
Exchange sendExchange = ExchangeBuilder.anExchange(camelContext).withBody(inputReq).build();
Exchange outExchange = producer.send(sendExchange);
String outString = outExchange.getMessage().getBody(String.class);
System.out.println(outString); //this will have a value of "text" since I am setting the body as "text" in the second route
return outString;
}
}

Spring Boot Camel Route - get data from rest endpoint

I want to create camel route in Spring Boot (2.1.1) project to get the data from some (rest) endpoint (http://localhost:8080/getAllUsers) and to send that data to activeMq.
I have tried with timer data to send it on activeMq and to consume it and it is working. But I have problem with collecting data from endpoint.
I have tried several things but no success. This is what I have tried.
In this example I am not sending the data to ActiveMq, I just want to see the response...
public void createNewRoute() {
CamelContext context = new DefaultCamelContext();
try {
ProducerTemplate template = context.createProducerTemplate();
context.start();
Exchange exchange = template.request("http://localhost:8080/getAllUsers",
new Processor() {
public void process(Exchange exchange) throws Exception {
}
});
if (null != exchange) {
Message out = exchange.getOut();
int responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
System.out.println("Response: " + String.valueOf(responseCode));
}
Thread.sleep(1000 * 3);
context.stop();
} catch (Exception ex) {
System.out.println("Exception: " + ex);
}
System.out.println("DONE!!");
}
Another route:
from("servlet://localhost:8080/getAllUsers").to("activemq://all-users");
And another:
rest("//localhost:8080/getAllUsers")
.get().consumes("application/json")
.to("activemq://all-users");
I will go with your second example:
from("timer://test?repeatCount=1").routeId("newRoute")
.streamCaching()
.process(exchange -> exchange.getIn()
.setBody(exchange.getIn()
.getBody()))
.marshal()
.json(JsonLibrary.Jackson)
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("http://localhost:8080/getAllUsers")
.log(LoggingLevel.INFO, "This is my body: ${body}")
.to("activemq:queue://new-queue");
This will trigger it once.
Try this without context.start() ....
CamelContext camelContext = new DefaultCamelContext();
ProducerTemplate template = camelContext.createProducerTemplate();
Exchange exchange = template.send("http://localhost:8080/getAllUsers", new Processor() {
public void process(Exchange exchange) throws Exception {}
});
Message out = exchange.getOut();
The http components are streaming based, so you can ask Camel to give you the response as string instead.
String s = exchange.getMessage().getBody(String.class);
See more in these links
http://camel.apache.org/stream-caching
http://camel.apache.org/why-is-my-message-body-empty.html

Request response over HTTP with Spring and activemq

I am building a simple REST api which connects a web server to a back end service, which performs a simple check and sends a response.
So client (over HTTP) -> to Web Server (over ACTIVEMQ/CAMEL)-> to Checking-Service, and back again.
The endpoint for the GET request is "/{id}". I'm trying to make this send a message through queue:ws-out to queue:cs-in and map it all the way back again to the original GET request.
The Checking-Service (cs) code is fine, it simply changes a value in the CheckMessage object to true using jmslistener.
I've searched the web thoroughly for examples, but can't get anything to work. The closest one I found was the following.
This is what I have so far on the Web Server (ws).
RestController
import ...
#RestController
public class RESTController extends Exception{
#Autowired
CamelContext camelContext;
#Autowired
JmsTemplate jmsTemplate;
#GetMapping("/{id}")
public String testCamel(#PathVariable String id) {
//Object used to send out
CheckMessage outMsg = new CheckMessage(id);
//Object used to receive response
CheckMessage inMsg = new CheckMessage(id);
//Sending the message out (working)
jmsTemplate.convertAndSend("ws-out", outMsg);
//Returning the response to the client (need correlation to the out message"
return jmsTemplate.receiveSelectedAndConvert("ws-in", ??);
}
}
Listener on ws
#Service
public class WSListener {
//For receiving the response from Checking-Service
#JmsListener(destination = "ws-in")
public void receiveMessage(CheckMessage response) {
}
}
Thanks!
your receive messages from "ws-in" with 2 consumers jmsTemplate.receiveSelectedAndConvert and WSListener !! message from a queue is consumed by one of the 2.
you send messages to "ws-out" and consume from "ws-in" ?? last queue
is empty and not receive any message, you have to send messages to
it
you need a valid selector to retrieve the message with receiveSelectedAndConvert based on JMSCorrelationID as the example you mntioned or the id received from the rest request but you need to add this id to the message headers like below
this.jmsTemplate.convertAndSend("ws-out", id, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
TextMessage tm = session.createTextMessage(new CheckMessage(id));
tm.setJMSCorrelationID(id);
return tm;
}
});
return jmsTemplate.receiveSelectedAndConvert("ws-in", "JMSCorrelationID='" + id+ "'");
forward messages from "ws-out" to "ws-in"
#Service
public class WSListener {
//For receiving the response from Checking-Service
#JmsListener(destination = "ws-out")
public void receiveMessage(CheckMessage response) {
jmsTemplate.convertAndSend("ws-in", response);
}
}

Retrieve JMS Headers right after the message is sent without consuming the message

How can I retrieve JMS Message headers after sending the message but without consuming the message ?
this is my message sending code
jmsTemplate.convertAndSend(que, text, message -> {
LOGGER.info("setting JMS Message header values");
message.setStringProperty(RequestContext.HEADER_ID, id);
// LOGGER.info(message.getJMSMessageId()); -- this gives a null value here
return message;
});
the message headers get generated only after the message is posted to the queue so iis there a simple way to retrieve JMS message headers when using MessagePostProcessor?
I've referred the links - here and here but not much of help :(
You can't get the JmsMessageID header until the message is actually sent; the post processor just allows you to modify the converted message JUST BEFORE it is sent.
However, your second link should work ok, since it saves off a reference to the message which you can access later.
EDIT
Confirmed:
#SpringBootApplication
public class So48001045Application {
public static void main(String[] args) {
SpringApplication.run(So48001045Application.class, args).close();
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
final AtomicReference<Message> msg = new AtomicReference<>();
template.convertAndSend("foo", "bar", m -> {
msg.set(m);
return m;
});
System.out.println(msg.get().getJMSMessageID());
};
}
}
and
ID:host.local-61612-1514496441253-4:1:1:1:1

Resources