This is my current setup:
queue1 and queue2 are marged together with integration flow to channel1:
#Bean
public IntegrationFlow q1f() {
return IntegrationFlows
.from(queue1InboundAdapter())
...
.channel(amqpInputChannel())
.get();
}
#Bean
public IntegrationFlow q2f() {
return IntegrationFlows
.from(queue2InboundAdapter())
...
.channel(amqpInputChannel())
.get();
}
then, everything is aggregated and then confirmed after aggregated message is confirmed by rabbitmq:
#Bean
public IntegrationFlow aggregatingFlow() {
return IntegrationFlows
.from(amqpInputChannel())
.aggregate(...
.expireGroupsUponCompletion(true)
.sendPartialResultOnExpiry(true)
.groupTimeout(TimeUnit.SECONDS.toMillis(10))
.releaseStrategy(new TimeoutCountSequenceSizeReleaseStrategy(200, TimeUnit.SECONDS.toMillis(10)))
)
.handle(amqpOutboundEndpoint())
.get();
}
#Bean
public AmqpOutboundEndpoint amqpOutboundEndpoint() {
AmqpOutboundEndpoint outboundEndpoint = new AmqpOutboundEndpoint(ackTemplate());
outboundEndpoint.setConfirmAckChannel(manualAckChannel());
outboundEndpoint.setConfirmCorrelationExpressionString("#root");
outboundEndpoint.setExchangeName(RABBIT_PREFIX + "ix.archiveupdate");
outboundEndpoint.setRoutingKeyExpression(routingKeyExpression()); //forward using patition id as routing key
return outboundEndpoint;
}
ackTemplate() is set with cf that has springFactory.setPublisherConfirms(true);.
The problem I see is that once in 10 days, there are some messages that are stuck in unacknowledged state in rabbitmq.
My guess is that somehow publish of message is waiting for rabbit to do PUBLISHER CONFIRMS but it never gets it and times out? In this case, I never ACK message in queue1. Is this possible?
So just one more time complete workflow:
[two queues -> direct channel -> aggregator (keeps channel and tag values) -> publish to rabbit -> rabbit returns ACK via publisher confirms -> spring confirms all messages on channel+values that it kept in memory for aggregated message]
I also have my implementation of aggregator (since I need to manually ack messages from both q1 and q2):
public abstract class AbstractManualAckAggregatingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
public static final String MANUAL_ACK_PAIRS = PREFIX + "manualAckPairs";
private AckingState ackingState;
public AbstractManualAckAggregatingMessageGroupProcessor(AckingState ackingState){
this.ackingState = ackingState;
}
#Override
protected Map<String, Object> aggregateHeaders(MessageGroup group) {
Map<String, Object> aggregatedHeaders = super.aggregateHeaders(group);
List<ManualAckPair> manualAckPairs = new ArrayList<>();
group.getMessages().forEach(m -> {
Channel channel = (Channel)m.getHeaders().get(AmqpHeaders.CHANNEL);
Long deliveryTag = (Long)m.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
manualAckPairs.add(new ManualAckPair(channel, deliveryTag, ackingState));
});
aggregatedHeaders.put(MANUAL_ACK_PAIRS, manualAckPairs);
return aggregatedHeaders;
}
}
UPDATE
This is how rabbit admin looks (2 unacked messages for a long time, and it will not be ACKED untill restart - when it is redelivered):
In Spring AMQP version 2.1 (Spring Integration 5.1), We added a Future<?> and returned message to the CorrelationData to assist with this kind of thing. If you are using an older version, you can subclass CorrelationData (and you'd have to handle setting the future and returned message in your code).
This, together with a scheduled task, can detect missing acks...
#SpringBootApplication
#EnableScheduling
public class Igh2755Application {
public static void main(String[] args) {
SpringApplication.run(Igh2755Application.class, args);
}
private final BlockingQueue<CorrelationData> futures = new LinkedBlockingQueue<>();
#Bean
public ApplicationRunner runner(RabbitTemplate template) {
return args -> {
SuccessCallback<? super Confirm> successCallback = confirm -> {
System.out.println((confirm.isAck() ? "A" : "Na") + "ck received");
};
FailureCallback failureCallback = throwable -> {
System.out.println(throwable.getMessage());
};
// Good - ack
CorrelationData correlationData = new CorrelationData("good");
correlationData.getFuture().addCallback(successCallback, failureCallback);
this.futures.put(correlationData);
template.convertAndSend("", "foo", "data", correlationData);
// Missing exchange nack, no return
correlationData = new CorrelationData("missing exchange");
correlationData.getFuture().addCallback(successCallback, failureCallback);
this.futures.put(correlationData);
template.convertAndSend("missing exchange", "foo", "data", correlationData);
// Missing queue ack, with return
correlationData = new CorrelationData("missing queue");
correlationData.getFuture().addCallback(successCallback, failureCallback);
this.futures.put(correlationData);
template.convertAndSend("", "missing queue", "data", correlationData);
};
}
#Scheduled(fixedDelay = 5_000)
public void checkForMissingAcks() {
System.out.println("Checking pending acks");
CorrelationData correlationData = this.futures.poll();
while (correlationData != null) {
try {
if (correlationData.getFuture().get(10, TimeUnit.SECONDS).isAck()) {
if (correlationData.getReturnedMessage() == null) {
System.out.println("Ack received OK for " + correlationData.getId());
}
else {
System.out.println("Message returned for " + correlationData.getId());
}
}
else {
System.out.println("Nack received for " + correlationData.getId());
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Interrupted");
}
catch (ExecutionException e) {
System.out.println("Failed to get an ack " + e.getCause().getMessage());
}
catch (TimeoutException e) {
System.out.println("Timed out waiting for ack for " + correlationData.getId());
}
correlationData = this.futures.poll();
}
System.out.println("No pending acks, exiting");
}
}
.
Checking pending acks
Ack received OK for good
Nack received for missing exchange
Message returned for missing queue
No pending acks, exiting
With Spring Integration there is a confirmCorrelationExpression which can be used to create the CorrelationData instance.
EDIT
With Spring Integration...
#SpringBootApplication
#EnableScheduling
public class Igh2755Application {
public static void main(String[] args) {
SpringApplication.run(Igh2755Application.class, args);
}
private final BlockingQueue<CorrelationData> futures = new LinkedBlockingQueue<>();
public interface Gate {
void send(#Header("exchange") String exchange, #Header("rk") String rk, String payload);
}
#Bean
#DependsOn("flow")
public ApplicationRunner runner(Gate gate) {
return args -> {
gate.send("", "foo", "good");
gate.send("junque", "rk", "missing exchange");
gate.send("", "junque", "missing queue");
};
}
#Bean
public IntegrationFlow flow(RabbitTemplate template) {
return IntegrationFlows.from(Gate.class)
.handle(Amqp.outboundAdapter(template)
.confirmCorrelationExpression("#correlationCreator.create(#root)")
.exchangeNameExpression("headers.exchange")
.routingKeyExpression("headers.rk")
.returnChannel(returns())
.confirmAckChannel(acks())
.confirmNackChannel(acks()))
.get();
}
#Bean
public MessageChannel acks() {
return new DirectChannel();
}
#Bean
public MessageChannel returns() {
return new DirectChannel();
}
#Bean
public IntegrationFlow ackFlow() {
return IntegrationFlows.from("acks")
/*
* Work around a bug because the correlation data is wrapped and so the
* wrong future is completed.
*/
.handle(m -> {
System.out.println(m);
if (m instanceof ErrorMessage) { // NACK
NackedAmqpMessageException nme = (NackedAmqpMessageException) m.getPayload();
CorrelationData correlationData = (CorrelationData) nme.getCorrelationData();
correlationData.getFuture().set(new Confirm(false, "Message was returned"));
}
else {
((CorrelationData) m.getPayload()).getFuture().set(new Confirm(true, null));
}
})
.get();
}
#Bean
public IntegrationFlow retFlow() {
return IntegrationFlows.from("returns")
.handle(System.out::println)
.get();
}
#Bean
public CorrelationCreator correlationCreator() {
return new CorrelationCreator(this.futures);
}
public static class CorrelationCreator {
private final BlockingQueue<CorrelationData> futures;
public CorrelationCreator(BlockingQueue<CorrelationData> futures) {
this.futures = futures;
}
public CorrelationData create(Message<String> message) {
CorrelationData data = new CorrelationData(message.getPayload());
this.futures.add(data);
return data;
}
}
#Scheduled(fixedDelay = 5_000)
public void checkForMissingAcks() {
System.out.println("Checking pending acks");
CorrelationData correlationData = this.futures.poll();
while (correlationData != null) {
try {
if (correlationData.getFuture().get(10, TimeUnit.SECONDS).isAck()) {
if (correlationData.getReturnedMessage() == null
&& !correlationData.getId().equals("Message was returned")) {
System.out.println("Ack received OK for " + correlationData.getId());
}
else {
System.out.println("Message returned for " + correlationData.getId());
}
}
else {
System.out.println("Nack received for " + correlationData.getId());
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Interrupted");
}
catch (ExecutionException e) {
System.out.println("Failed to get an ack " + e.getCause().getMessage());
}
catch (TimeoutException e) {
System.out.println("Timed out waiting for ack for " + correlationData.getId());
}
correlationData = this.futures.poll();
}
System.out.println("No pending acks, exiting");
}
}
you can declare connection as bean
#Bean
public ConnectionFactory createConnectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("127.0.0.1", 5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
connectionFactory.setPublisherReturns(true);
connectionFactory.setPublisherConfirmType(ConfirmType.SIMPLE);
return connectionFactory;
}
Then RabbitTemplate as
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(callback);
return rabbitTemplate;
}
Where callback is implementation of ConfirmCallback interface
and while sending you can just wait for confirmation
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(rabbitMQProperties.getEXCHANGENAME(),
rabbitMQProperties.getQUEUENAME(), "hello from rabbit");
rabbitTemplate.waitForConfirms(1);
waitforconfirms will take time in milisecond. I put it as 1 for testing purpose.
Related
Is it possible to pause and start a GCP PubSub Subscriber(pull) programatically using Java?
I have the following code for the MessageReceiver:
MessageReceiver receiver = (message, consumer) -> {
System.out.println("Received message: " + message.getData().toStringUtf8() + " at " + LocalDateTime.now());
Mono<Void> mono = Mono.just(message.getData().toStringUtf8())
.flatMap(deserializePayload())
.doOnSuccess(creditNoteRequestDTO -> configureMDC(message.getAttributesMap(), creditNoteRequestDTO.getOrderNumber()))
.doOnError(error -> log.error("Problem while parsing CreditNoteRequest DTO {}", message.getData().toStringUtf8(), error))
.map(creditNoteRequestMapper::mapToCreditNoteRequest)
.flatMap(creditNoteRequestProcess::process)
.doOnError(throwable -> {
if (throwable instanceof CallNotPermittedException) ConstantUtils.ENABLED_SUBSCRIBER = false;
consumer.nack();
})
.doOnSuccess(unused -> {
log.info("Doing ACK on payload {}", message.getData().toStringUtf8());
consumer.ack();
});
if (Boolean.TRUE.equals(ConstantUtils.ENABLED_SUBSCRIBER)) {
mono.subscribe();
} else {
System.err.println("Subscription has been disabled.");
//mono.subscribe().dispose();
try {
System.err.println("PRE: " + LocalDateTime.now() + " " + ConstantUtils.ENABLED_SUBSCRIBER);
TimeUnit.SECONDS.sleep(15);
ConstantUtils.ENABLED_SUBSCRIBER = true;
System.err.println("POST: " + LocalDateTime.now() + " " + ConstantUtils.ENABLED_SUBSCRIBER);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
And the following for the subscriber:
Subscriber subscriber = null;
FlowControlSettings controlSettings = FlowControlSettings
.newBuilder()
.setMaxOutstandingElementCount(2000L)
.build();
subscriber = Subscriber
.newBuilder(subscription, receiver)
.setFlowControlSettings(controlSettings)
.build();
// Start the subscriber.
subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", subscription);
System.out.printf("Max elements count: %s\n", subscriber.getFlowControlSettings().getMaxOutstandingElementCount());
// Allow the subscriber to run for 45s unless an unrecoverable error occurs.
Subscriber finalSubscriber = subscriber;
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
System.err.println("Subscriber has been shut down.");
finalSubscriber.stopAsync();
}
},
120000
);
It is somehow possible to use finalSubscriber.stopAsync(); on the MessageReceiver or pause the messageReceiver?
UPDATE: Thanks, I managed to stop it, but how do I restart it? Now, when I try to restart, nothing happens.
private void createSubscriber() {
ProjectSubscriptionName subscription = ProjectSubscriptionName.of("txd-boss-dev", "circuit-breaker-test-sub");
this.subscriber = Subscriber.newBuilder(subscription, getMessageReceiver()).build();
}
private void runSubscriber(boolean start) {
if(start) {
try {
this.subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", this.subscriber.getSubscriptionNameString());
subscriber.awaitTerminated(240, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.err.println("Shutting down subscription.");
runSubscriber(false);
}
} else {
subscriber.stopAsync();
}
}
#EventListener(ContextRefreshedEvent.class)
public void test() {
createSubscriber();
runSubscriber(true);
}
You need to return the same subscriber object to start and stop it:
check some google examples here.
here is a sketch (adapt for your class):
private Subscriber StartSubscriber() {
Subscriber subscriber = null;
try {
subscriber = Subscriber.newBuilder(subscriptionName, receiver).build();
subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", subscriptionName.toString());
subscriber.awaitTerminated(30, TimeUnit.SECONDS);
return subscriber;
} catch (TimeoutException timeoutException) {
// Shut down the subscriber after 30s. Stop receiving messages.
System.out.printf("Error \n");
subscriber.stopAsync();
}
}
private void StopSubscriber(Subscriber subscriber) {
System.out.printf("Stoping subscriber\n");
subscriber.stopAsync();
}
public void test() {
Subscriber subscriber = StartSubscriber();
StopSubscriber(subscriber);
}
I am trying to send data over event bus when got the call from client and response back to client.
Everything is working fine until i add the interceptor on event bus..
Here is the Code:-
public class TestVerticle extends AbstractVerticle {
#Override
public void start(Promise<Void> promise) {
Router router = Router.router(vertx);
vertx.exceptionHandler(globalExceptionHandler -> {
System.out.println("Exception not handled in application : " + globalExceptionHandler.getCause());
});
vertx.eventBus().consumer("test", handler -> {
System.out.println("Message receive form event bus : " + handler.body().toString());
handler.reply(handler.body().toString());
});
router.get().handler(this::rootHandler);
vertx.createHttpServer().requestHandler(router).listen(8080, resultHandler -> {
if (resultHandler.succeeded()) {
promise.complete();
} else {
promise.fail(resultHandler.cause());
}
});
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
});
// vertx.eventBus().addInboundInterceptor(handler -> {
// System.out.println("Inbound data : " + handler.body().toString());
// });
}
private void rootHandler(RoutingContext routingContext) {
JsonObject msg = new JsonObject().put("path", routingContext.request().path());
vertx.eventBus().request("test","Hello from Application",reply -> this.replyHandler(routingContext,reply));
}
private void replyHandler(RoutingContext ctx, AsyncResult<Message<Object>> reply) {
HttpServerResponse response = ctx.response()
.putHeader("Content-Type", "application/json");
System.out.println("Reply from event bus : " +reply.result().body().toString());
if (reply.succeeded()) {
response.setStatusCode(200)
.setStatusMessage("OK")
.end(reply.result().body().toString());
} else {
response.setStatusCode(500)
.setStatusMessage("Server Error")
.end(new JsonObject().put("error", reply.cause().getLocalizedMessage()).encodePrettily());
}
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(new TestVerticle(), deploymenHandler -> {
if (deploymenHandler.succeeded()) {
System.out.println("verticle deplyed");
} else {
System.out.println("failed");
}
});
}
#Override
public void stop(Promise<Void> promise) {
System.out.println("Exiting verticle");
promise.complete()
}
}
One more doubt is when I stop the application from IDE the Stop method is not called, but If I undeploy this verticle from another verticle that is working fine.
When you are using eventbus interceptors you will have to call the next() method in the handler otherwise you will get stuck there.
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
handler.next();
});
vertx.eventBus().addInboundInterceptor(handler -> {
System.out.println("Inbound data : "+handler.body().toString());
handler.next();
});
My code is in java + Spring Boot
#Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void produce(String message) {
logger.info("Producer : Kafka Topic -> {}, Kafka Message -> {}", TOPIC, message);
kafkaTemplate.send(TOPIC, message);
}
#KafkaListener(topics = TOPIC, groupId = GROUP_ID)
public void consume(String message) {
System.out.println("Kafka consume value ->" + message);
logger.info("Consumer : Kafka Message -> {}", message);
try {
setKafkaStatus(Integer.parseInt(message.trim()));
}catch (Exception e) {
logger.info("Kafka message is not Integer");
setKafkaStatus(0);
}
}
public void closeConnection() {
//code for close connection
}
#Autowired
private KafkaListenerEndpointRegistry registry;
public void closeConnection() {
this.registry.stop();
}
I am unable to catch thrown exceptions from an async method in Spring. I have written an uncaught exception handler to catch but was unsuccessful.
The application will enable to start any number of forever running asynchronous jobs.
I think my async method needs to return Future so that I can store it in hashmap and check its status or stop the job. I also can get all running jobs by storing it.
I think I can't use get method of future because if the input is correct it blocks and my job will be forever running. I need to send status as started if the input is fine. Whenever an exception occurs in the Async method it is thrown but I am unable to catch it. How can I do that?
Here is my complete code.
Application.java
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AsyncConfig.java
#EnableAsync
#Configuration
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
AsyncExceptionHandler.java
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("Exception Cause - " + throwable.getMessage());
System.out.println("Method name - " + method.getName());
for (Object param : obj) {
System.out.println("Parameter value - " + param);
}
}
}
createBucket.java
#Service
public class createBucket {
#Async
public Future<String> start(String config){
try {
JSONObject map = new JSONObject(config);
Jedis jedis = new Jedis(map.getString("jedisip"));
jedis.auth(map.getString("password"));
// code to make a kafka consumer subscribe to a topic given in config input
while(true) {
//forever running code which polls using a kafka consumer
}
}
catch(JedisException j) {
throw new JedisException("Some msg");
}
}
}
Endpoint.java
#Controller
public class Endpoint {
#Autowired
private createBucket service;
private Future<String> out;
private HashMap<String, Future<String>> maps = new HashMap<>();
#PostMapping(value = "/start", consumes = "application/json", produces = "application/json")
public ResponseEntity<String> starttask(#RequestBody String conf) {
try {
out = service.start(conf);
maps.put(conf, out);
}
catch (Exception e) {
return new ResponseEntity<>("exception", HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("{\"started\":\"true\"}", HttpStatus.CREATED);
}
}
As stated in official doc, AsyncUncaughtExceptionHandler is used for void return value.
https://docs.spring.io/spring/docs/5.1.10.RELEASE/spring-framework-reference/integration.html#spring-integration
In your scenario, I recommend using CompletableFuture and DeferredResult:
#Async
public CompletableFuture<String> start(String config) {
CompletableFuture completableFuture = new CompletableFuture();
try {
JSONObject map = new JSONObject(config);
Jedis jedis = new Jedis(map.getString("jedisip"));
jedis.auth(map.getString("password"));
completableFuture.complete("started!");
}
catch(JedisException j) {
completableFuture.completeExceptionally(j);
}
return completableFuture;
}
#PostMapping(value = "/start", consumes = "application/json", produces = "application/json")
public DeferredResult<ResponseEntity> starttask(#RequestBody String conf) {
CompletableFuture<String> start = service.start(conf);
DeferredResult<ResponseEntity> deferredResult = new DeferredResult<>();
start.whenComplete((res, ex) -> {
if (ex == null) {
ResponseEntity<String> successEntity = new ResponseEntity<>("{\"started\":\"true\"}", HttpStatus.CREATED);\
deferredResult.setResult(successEntity);
} else {
// handle ex here!
ResponseEntity<String> exEntity = new ResponseEntity<>("exception", HttpStatus.BAD_REQUEST);
deferredResult.setResult(exEntity);
}
});
return deferredResult;
}
There is another serious problem. The following code is not thread safe.
private Future<String> out;
private HashMap<String, Future<String>> maps = new HashMap<>();
i have a requirement where i need to send a email but if the email server is down or any error occurs while sending a email that need to be retry for a specific number of times
below is my bean properties
#Bean(destroyMethod = "")
public JndiTemplate jndiTemplate() {
Properties environment = new Properties();
environment.put(Context.INITIAL_CONTEXT_FACTORY, env.getProperty("XXXXXX"));
environment.put(Context.PROVIDER_URL, env.getProperty("XXXXXX"));
JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(environment);
return jndiTemplate;
}
#Bean(destroyMethod = "")
public JndiObjectFactoryBean jmsConnFactory() {
JndiObjectFactoryBean jmsConnFactory = new JndiObjectFactoryBean();
jmsConnFactory.setJndiTemplate(jndiTemplate());
jmsConnFactory.setJndiName(env.getProperty("XXXXX"));
return jmsConnFactory;
}
#Bean(destroyMethod = "")
public JndiObjectFactoryBean jmsDestanation() {
JndiObjectFactoryBean destination = new JndiObjectFactoryBean();
destination.setJndiTemplate(jndiTemplate());
destination.setJndiName(env.getProperty("XXXXXX"));
return destination;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDefaultDestination(jmsDestanation());
jmsTemplate.setConnectionFactory(jmsConnFactory());
return jmsTemplate;
}
#Bean
public JmsReceiver jmsReciver() {
return new JmsReceiver();
}
#Bean
public JmsExceptionListener jmsExceptionListener(){
return new JmsExceptionListener();
}
#Bean
public JmsErrorHandleListener jmsErrorHandleListener(){
return new JmsErrorHandleListener();
}
#Bean
public DefaultMessageListenerContainer jmsQueueListner() {
DefaultMessageListenerContainer listner = new DefaultMessageListenerContainer();
listner.setDestination(jmsDestanation());
listner.setConnectionFactory(jmsConnFactory());
listner.setMessageListener(jmsReciver());
listner.setExceptionListener(jmsExceptionListener());
listner.setErrorHandler(jmsErrorHandleListener());
return listner;
}
and below is my Listener class and error class
public class JmsReceiver implements MessageListener {
#Autowired
JavaMailSender jMailsender;
#Override
public void onMessage(Message message) {
TextMessage text = (TextMessage) message;
ObjectMapper objectMapper = new ObjectMapper();
MimeMessage mimeMessage = jMailsender.createMimeMessage();
try {
JmsMessage inMessage = objectMapper.readValue(text.getText(), JmsMessage.class);
//this is failing and go to the JmsErrorHandleListener
jMailsender.send(mimeMessage);
} catch (JMSException | IOException | MessagingException ex) {
logger.error("Exception on reading message ",ex);
}
}
}
public class JmsErrorHandleListener implements ErrorHandler {
#Override
public void handleError(Throwable t) {
/// not sure how to retry from hear becoz the message was allready read
/// some how i need to inform the weblogic this message was not read yet
}
}
when the message arrives to the onMessage it will throw an error then executes the JmsErrorHandleListener but since the message is already read im not sure how to call the send method again and again
try with below config, spring DMLC manage Exception's to retry MessageListener execution, if jMailsender.send(mimeMessage); fails JmsReceiver.onMessage will be retried 5s later indefintely, see DMLC backoff property
#Bean
public org.springframework.jms.listener.adapter.MessageListenerAdapter jmsReciver() {
return new org.springframework.jms.listener.adapter.MessageListenerAdapter(receiver());
}
#Bean
public JmsReceiver receiver() {
return new JmsReceiver();
}
public class JmsReceiver {
#Autowired
JavaMailSender jMailsender;
#Override
public void onMessage(Message message) throws JMSException {
TextMessage text = (TextMessage) message;
ObjectMapper objectMapper = new ObjectMapper();
MimeMessage mimeMessage = jMailsender.createMimeMessage();
try {
JmsMessage inMessage = objectMapper.readValue(text.getText(), JmsMessage.class);
//this is failing and go to the JmsErrorHandleListener
jMailsender.send(mimeMessage);
} catch (Throwable ex) {
logger.error("Exception on reading message ",ex);
throw new JMSException(ex.getMessage());
}
}
}