I have a Spring Boot application that has a JMS Publisher. The publisher used retry logic that was in the publisher class that worked fine. But I want to change that to the Spring Retry Template and put it into the connection factory configuration.
I am having trouble figuring out how to call add the RetryTemplate into the Publisher class.
This is the Configuration class:
@Configuration
@EnableRetry
public class PurchasedTransServicePublisherConfig {
@Value("${java.naming.factory.initial.publisher}")
private String context;
@Value("${java.naming.provider.url.publisher}")
private String providerURL;
@Value("${fedex.jms.LDAP.entryName.publisher}")
private String ldapEntryName;
private @Value("${jms.username.publisher:#{null}}") String jmsUserName;
private @Value("${jms.password.publisher:#{null}}") String jmsPassword;
@Value("${jms.destinationname.publisher}")
private String destinationName;
private static final Logger LOGGER = LoggerFactory.getLogger(PurchasedTransServicePublisherConfig.class);
@Autowired(required = false)
FxgCipherInitializer jmsParams;
@Bean
public JmsTemplate publisherJmsTemplate(final ConnectionFactory publisherConnectionFactory) {
final JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(publisherConnectionFactory);
jmsTemplate.setPubSubDomain(true);
jmsTemplate.setDefaultDestinationName(destinationName);
jmsTemplate.setSessionTransacted(true);
return jmsTemplate;
}
@Bean
public ConnectionFactory publisherConnectionFactory(final JndiTemplate publisherJndiTemplate) throws NamingException {
final ConnectionFactory connectionFactory = (ConnectionFactory) publisherJndiTemplate.getContext().lookup(ldapEntryName);
final UserCredentialsConnectionFactoryAdapter ucf = new UserCredentialsConnectionFactoryAdapter();
ucf.setUsername(((null != jmsParams) ? jmsParams.getUsername() : jmsUserName));
ucf.setPassword((null != jmsParams) ? jmsParams.getPassword() : jmsPassword);
ucf.setTargetConnectionFactory(connectionFactory);
return ucf;
}
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(2000l);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(2);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
@Bean
public JndiTemplate publisherJndiTemplate() {
final JndiTemplate jndiTemplate = new JndiTemplate();
final Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, context);
jndiProps.setProperty(Context.PROVIDER_URL, providerURL);
jndiTemplate.setEnvironment(jndiProps);
return jndiTemplate;
}
}
The only change between the working configuration and the RetryTemplate configuration is the addition of the annotation @EnableRetry
and the method retryTemplate
The publisher class originally successfully retried the send messsage using this logic:
private void send(final MessageCreator messageCreator) throws JMSException {
int sendAttempts = 0;
while (true) {
try {
jmsTemplate.send(messageCreator);
LOGGER.info("Message Successfully Published");
break;
} catch (RuntimeException e) {
LOGGER.error("Caught Runtime Exception: {}", e.getMessage());
sendAttempts++;
handleJmsExceptionRetry(e, sendAttempts);
}
}
}
I tried to implement the retry template like this:
private void send(final MessageCreator messageCreator) throws JMSException {
while (true) {
try {
publisherJmsTemplate.send(messageCreator);
LOGGER.info("Message Successfully Published");
break;
} catch (RuntimeException e) {
LOGGER.error("Caught Runtime Exception: {}", e.getMessage());
publisherRetryTemplate.execute(arg0 -> {
publisherJmsTemplate.send(messageCreator);
return null;
});
}
}
}
The test method that I created to unit test this is below:
@Test
public void testPublishTmsTrip_WhenPublishFailsMultipleTimes() {
Mockito.doThrow(RuntimeException.class).when(mockJmsTemplate).send(mockMessageCreator);
boolean testBoolean = tmsTripPublisher.publishTmsTripMessageEvent("TEST message");
assertFalse(testBoolean);
}
The problem is when it gets to the publisherRetryTemplate.execute...
, it does not execute the RetryTemplate method.
Any guidance into how to implement this retry logic would be greatly appreciated.