Spring boot #Retryable not working in service class - spring-boot

I'm trying to add retry logic in my application for sending mail to respective users through rest controller and i have annotate #EnableRetry in my SpringbootApplication class file
#RestController
public class TserviceController {
#Autowired
private Tservice tService ;
#RequestMapping(method = RequestMethod.GET, value = "/sendMail")
public Object sayHello(HttpServletResponse response) throws IOException {
try{
boolean t = tService.sendConfirmationMail();
}catch(Exception e){
System.out.println("--> rest failed");
return ResponseEntity.status(500).body("error");
}
return ResponseEntity.status(200).body("success");
}
}
My Tservice.class
#Service
public class Tservice {
private JavaMailSender javaMailSender;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
public Tservice(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
#Retryable(backoff = #Backoff(delay = 5000), maxAttempts = 3)
public boolean sendConfirmationMail() throws Exception {
try{
System.out.println("--> mail service calling");
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(toEmail);
mailMessage.setSubject(subject);
mailMessage.setText(message);
mailMessage.setFrom(emailFrom);
javaMailSender.send(mailMessage);
return true;
}catch(Exception e){
throw new Exception(e);
}
}
#Recover
public void recover(Exception ex) {
System.out.println("--> service failed");
}
}
When i try to run the /sendMail and whenever exception arise in service class it retrying 3 times successfully but after reaching the maxattempts, i'm getting the console print as below
--> mail service calling
--> mail service calling
--> mail service calling
--> rest failed
instead of printing
--> service failed
Here what im doing wrong..?

As per Javadoc for #Recover your recover method must have the same return type as the Retryable method.
So it should be
#Recover
public boolean recover(Exception ex) {
System.out.println("--> service failed");
return false;
}
JavaDoc:
A suitable recovery handler has a first parameter of type Throwable (or a subtype of Throwable) and a return value of the same type as the #Retryable method to recover from.

Related

Spring jms invokes the wrong listener method when receiving a message

I am playing with Spring-boot and jms message driven beans.
I installed Apache ActiveMQ.
One queue is being used on which different message types are being send and read.
One simple MessageConverter was written to convert a POJO instance into XML.
A property Class was set in the message to determine how to convert a message to a POJO:
#Component
#Slf4j
public class XMLMessageConverter implements MessageConverter {
private static final String CLASS_NAME = "Class";
private final Map<Class<?>, Marshaller> marshallers = new HashMap<>();
#SneakyThrows
private Marshaller getMarshallerForClass(Class<?> clazz) {
marshallers.putIfAbsent(clazz, JAXBContext.newInstance(clazz).createMarshaller());
Marshaller marshaller = marshallers.get(clazz);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
return marshaller;
}
#Override
public Message toMessage(#NonNull Object object, Session session) throws JMSException, MessageConversionException {
try {
Marshaller marshaller = getMarshallerForClass(object.getClass());
StringWriter stringWriter = new StringWriter();
marshaller.marshal(object, stringWriter);
TextMessage message = session.createTextMessage();
log.info("Created message\n{}", stringWriter);
message.setText(stringWriter.toString());
message.setStringProperty(CLASS_NAME, object.getClass().getCanonicalName());
return message;
} catch (JAXBException e) {
throw new MessageConversionException(e.getMessage());
}
}
#Override
public Object fromMessage(#NonNull Message message) throws JMSException, MessageConversionException {
TextMessage textMessage = (TextMessage) message;
String payload = textMessage.getText();
String className = textMessage.getStringProperty(CLASS_NAME);
log.info("Converting message with id {} and {}={}into java object.", message.getJMSMessageID(), CLASS_NAME, className);
try {
Class<?> clazz = Class.forName(className);
JAXBContext context = JAXBContext.newInstance(clazz);
return context.createUnmarshaller().unmarshal(new StringReader(payload));
} catch (JAXBException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Messages of different type (OrderTransaction or Person) where send every 5 seconds to the queue:
#Scheduled(fixedDelay = 5000)
public void sendMessage() {
if ((int)(Math.random()*2) == 0) {
jmsTemplate.convertAndSend("DummyQueue", new OrderTransaction(new Person("Mark", "Smith"), new Person("Tom", "Smith"), BigDecimal.TEN));
}
else {
jmsTemplate.convertAndSend("DummyQueue", new Person("Mark", "Rutte"));
}
}
Two listeners were defined:
#JmsListener(destination = "DummyQueue", containerFactory = "myFactory")
public void receiveOrderTransactionMessage(OrderTransaction transaction) {
log.info("Received {}", transaction);
}
#JmsListener(destination = "DummyQueue", containerFactory = "myFactory")
public void receivePersonMessage(Person person) {
log.info("Received {}", person);
}
When I place breakpoints in the converter I see everything works fine but sometimes (not always) I get the following exception:
org.springframework.jms.listener.adapter.ListenerExecutionFailedException: Listener method could not be invoked with incoming message
Endpoint handler details:
Method [public void nl.smith.springmdb.configuration.MyListener.**receiveOrderTransactionMessage**(nl.smith.springmdb.domain.**OrderTransaction**)]
Bean [nl.smith.springmdb.configuration.MyListener#790fe82a]
; nested exception is org.springframework.messaging.converter.MessageConversionException: Cannot convert from [nl.smith.springmdb.domain.**Person**] to [nl.smith.springmdb.domain.**OrderTransaction**] for org.springframework.jms
It seems that after the conversion Spring invokes the wrong method.
I am complete in the dark why this happens.
Can somebody clarify what is happening?

Handling SOAP Fault in Spring Boot WebServiceTemplate

I am new to SOAP, trying to run a sample SOAP client using Spring Boot
How the SOAP fault, Exceptions or Errors are handled while using WebServiceTemplate
public class CountryClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(CountryClient.class);
public GetCountryResponse getCountry(String country) {
GetCountryRequest request = new GetCountryRequest();
request.setName(country);
log.info("Requesting location for " + country);
GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
.marshalSendAndReceive("http://localhost:8080/ws/countries", request,
new SoapActionCallback(
"http://spring.io/guides/gs-producing-web-service/GetCountryRequest"));
return response;
}
}
One way is writing your custom interceptor which implements Spring WS's ClientInterceptor interface. You should override handleFault method to handle SOAP faults with your custom logic.
public class MySoapClientInterceptor implements ClientInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MySoapClientInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
LOGGER.info("intercepted a fault...");
SoapBody soapBody = getSoapBody(messageContext);
SoapFault soapFault = soapBody.getFault();
LOGGER.error(soapFault.getFaultStringOrReason());
throw new RuntimeException(String.format("Error occured while invoking SOAP service - %s ", soapFault.getFaultStringOrReason()));
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
private SoapBody getSoapBody(MessageContext messageContext) {
SoapMessage soapMessage = (SoapMessage) messageContext.getResponse();
SoapEnvelope soapEnvelope = soapMessage.getEnvelope();
return soapEnvelope.getBody();
}
}
Then you need to register your custom Interceptor class as an interceptor at your SOAP client config class. At the bean definition of CountryClient at your Configuration class in your case.
#Configuration
public class SoapClientConfig {
#Value("${soap.server.url}")
public String soap_server_url;
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.example");
return marshaller;
}
#Bean
public SoapConnector soapConnector(Jaxb2Marshaller marshaller) {
SoapConnector client = new SoapConnector();
client.setDefaultUri(soap_server_url);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
ClientInterceptor[] clientInterceptors = {new MySoapClientInterceptor()};
client.setInterceptors(clientInterceptors);
return client;
}
}

Spring `#Autowire` field is `null` eventhough it works fine in other classes

Spring #Autowire field is null even though it works fine in other classes successfully.
public class SendRunner implements Runnable {
private String senderAddress;
#Autowired
private SubscriberService subscriberService;
public SendRunner(String senderAddress) {
this.senderAddress = senderAddress;
}
#Override
public void run() {
sendRequest();
}
private void sendRequest() {
try {
HashMap<String, String> dataMap = new HashMap<>();
dataMap.put("subscriberId", senderAddress);
HttpEntity<?> entity = new HttpEntity<Object>(dataMap, httpHeaders);
Subscriber subscriber = subscriberService.getSubscriberByMsisdn(senderAddress);
} catch (Exception e) {
logger.error("Error occurred while trying to send api request", e);
}
}
Also this class is managed as a bean in the dispatcher servlet :
<bean id="SendRunner" class="sms.dating.messenger.connector.SendRunner">
</bean>
In here i'm getting a null pointer exception for subscriberService. What would be the possible reason for this? Thanks in advance.
Can you please try with below code snippet
#Configuration
public class Someclass{
#Autowired
private SubscriberService subscriberService;
Thread subscriberThread = new Thread() {
#Override
public void run() {
try {
HashMap<String, String> dataMap = new HashMap<>();
dataMap.put("subscriberId", senderAddress);
HttpEntity<?> entity = new HttpEntity<Object>(dataMap, httpHeaders);
Subscriber subscriber = subscriberService.getSubscriberByMsisdn(senderAddress);
} catch (Exception e) {
logger.error("Error occurred while trying to send api request", e);
}
}
};
}
Can you please annotate your SendRunner class with #Component or #Service and include the SendRunner package in componentscanpackage
Your bean not in Spring Managed context, below can be the reasons.
Package sms.dating.messenger.connector not in Component scan.
You are moving out of the Spring context by creating an object with new (see below),
this way you will not get the autowired fields.
SendRunner sendRunner = new SendRunner () ,
sendRunner.sendRequest();
Just check how I implement. Hope this will help.
#RestController
public class RestRequest {
#Autowired
SendRunner sendRunner;
#RequestMapping("/api")
public void Uri() {
sendRunner.start();
}
}
SendRunner class
#Service
public class SendRunner extends Thread{
#Autowired
private SubscriberService subscriberService;
#Override
public void run() {
SendRequest();
}
private void SendRequest() {
System.out.println("Object is " + subscriberService);
String senderAddress = "address";
subscriberService.getSubscriberByMsisdn(senderAddress);
}
}
Below are the logs printed when I hit the REST api.
Object is com.example.demo.SubscriberService#40f33492

Overridden onMessage of MessageListner not getting called in Spring Kafka Consumer Unit Test

I am writing Kafka Consumer Unit Test, and need to Mock the Service of my KafkaConsumer for testing the Kafka Consumer independently. But, the mockObject of Service is not getting invoked, instead Spring is creating the original Service class object and calling it. Thus, my mock class object not getting called.
KafkaConsumer :
#Slf4j
#Component
#RequiredArgsConstructor (onConstructor = #__(#Autowired))
public class KafkaEventConsumer {
private final MyService requestService;
#KafkaListener (topics = "${kafka.topic:topic-name}")
public void receive(#Payload String message) throws Exception {
try {
LOGGER.debug("Received message:{} ", message);
ObjectMapper mapper = new ObjectMapper();
ForecastRequest forecastRequest = mapper.readValue(message, ForecastRequest.class);
JobDetail jobDetail = requestForecastService.refreshForecasts(forecastRequest);
if (jobDetail.getJobStatus() != JobStatus.complete) {
LOGGER.error("Failed to Refresh Forecast for ProgramId-{}, JobId-{}, JobStatus-{}",
forecastRequest.getProgramId(), jobDetail.getJobId(), jobDetail.getJobStatus());
throw new Exception("Internal Server Error");
}
} catch (Exception e) {
LOGGER.error("Failed to Refresh Forecast for Forecast Request {}", message, e);
throw e;
}
}
}
Kafka Consumer Test :
#RunWith (SpringRunner.class)
#ActiveProfiles ("kafkatest")
#SpringBootTest (classes = ForecastEventConsumerApplication.class)
#DirtiesContext
public class KafkaEventConsumerTest {
private static String TOPIC = "topic-name";
#Mock
private MyServiceImpl myServiceMock;
#InjectMocks
private KafkaEventConsumer kafkaEventConsumer;
private KafkaTemplate<String, String> template;
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
#ClassRule
public static final KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true,3, TOPIC);
#Before
public void setUp() throws Exception {
kafkaEventConsumer = new KafkaEventConsumer(myServiceMock);
// set up the Kafka producer properties
Map<String, Object> senderProperties = KafkaTestUtils.senderProps(embeddedKafka.getBrokersAsString());
// create a Kafka producer factory
ProducerFactory<String, String> producerFactory = new DefaultKafkaProducerFactory<String, String>(senderProperties);
// create a Kafka template
template = new KafkaTemplate<>(producerFactory);
// set the default topic to send to
template.setDefaultTopic(TOPIC);
// wait until the partitions are assigned
for (MessageListenerContainer messageListenerContainer : kafkaListenerEndpointRegistry.getListenerContainers()) {
messageListenerContainer.setupMessageListener(new MessageListener<String, String>() {
#Override
public void onMessage(ConsumerRecord<String, String> record) {
try {
kafkaEventConsumer.receive(record.value());
} catch (Exception e) {
e.printStackTrace();
}
}
});
ContainerTestUtils.waitForAssignment(messageListenerContainer, embeddedKafka.getPartitionsPerTopic());
}
}
#AfterClass
public static void tearDown() throws Exception {
embeddedKafka.destroy();
}
#Test
public void testReceive() throws Exception {
String forecastRequestMessage = "{\"programId\":100011770}";
ForecastRequest forecastRequest = ForecastRequest.builder().programId(100011770L).build();
JobDetail jobDetail = JobDetail.builder().jobStatus(JobStatus.complete).build();
Mockito.when(forecastServiceMock.refreshForecasts(Matchers.any())).thenReturn(jobDetail);
template.sendDefault(forecastRequestMessage);
Thread.sleep(2000L);
// validate something
}
}
The problem is, in the above #Test method instead of calling the mocked version of MyService it is calling the original MyService implementation. Also, while debugging my code I found that overridden onMessage() is also not getting called. Please help me in finding what am I doing wrong here.
You have to stop() all the MessageListenerContainers before calling their setupMessageListener(). Then you will need to start() them back to let them to pick up a fresh listener:
protected void doStart() {
...
Object messageListener = containerProperties.getMessageListener();
Assert.state(messageListener != null, "A MessageListener is required");
Anyway that sounds like you really would like to mock only your MyService which is injected into the real KafkaEventConsumer. So, how about to consider to use that like this:
#MockBean
private MyServiceImpl myServiceMock;
And you won't need to do anything in your #Before and no need in the #InjectMocks.
The KafkaEmbedded can expose its host/port (or brokers) properties to the expected Spring Boot conventional configuration properties like this:
#BeforeClass
public static void setup() {
System.setProperty("spring.kafka.bootstrap-servers", kafkaEmbedded.getBrokersAsString());
}
https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans

How to send email asynchronously using spring 4 #Async

I know this question has been asked but I am not able to send email using configuration. I don't know what I am doing wrong and why I am not getting the email. Here is my Spring configuration.
#Configuration
#PropertySource(value = {
"classpath:autoalert.properties"
})
#EnableAsync
#Import({PersistenceConfig.class, EmailConfig.class, VelocityConfig.class})
#ComponentScan(basePackageClasses = {
ServiceMarker.class,
RepositoryMarker.class }
)
public class AutoAlertAppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my email config
#Configuration
#PropertySources({
#PropertySource("classpath:email/email.properties")
})
public class EmailConfig {
#Autowired
private Environment env;
#Bean
public JavaMailSender mailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setSession(getEmailSession());
return mailSender;
}
#Bean
public MailMessage mailSettings() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(env.getProperty("mail.from"));
...
mailMessage.setText(env.getProperty("mail.body"));
return mailMessage;
}
private Session getEmailSession() {
Properties emailProperties = SpringUtil.loadPropertiesFileFromClassPath("email" + File.separator + "general-mail-settings.properties");
final String userName = emailProperties.getProperty("user");
final String password = emailProperties.getProperty("password");
Session session = null;
try {
session = Session.getInstance(emailProperties, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password);
}
}); //end of anonymous class
} catch (Exception e) {
e.printStackTrace();
}
return session;
} //end of getEmailSession()
}
Here is my class
public static void main(String[] args) {
try (GenericApplicationContext springContext = new AnnotationConfigApplicationContext(AutoAlertAppConfig.class)) {
AutoAlertService autoAlertService = springContext.getBean(AutoAlertServiceImpl.class);
try {
autoAlertService.handleProcess(fromDate, toDate);
} catch (Exception e) {
logger.error("Exception occurs", e);
autoAlertService.handleException(fromDate, toDate, e);
}
} catch (Exception e) {
logger.error("Exception occurs in loading Spring context: ", e);
}
#Service
public class AutoAlertServiceImpl implements AutoAlertService {
#Inject
private AsyncEmailService asyncEmailService;
#Override
public void handleProcess(String fromDate, String toDate) {
logger.info("Start process");
try {
..
//Sending email
asyncEmailService.sendMailWithFileAttachment(fromDate, toDate, file);
} catch (Exception e) {
handleException(fromDate, toDate, e);
}
logger.info("Finish process");
}
}
here is my email service
#Component
public class AsyncEmailServiceImpl implements AsyncEmailService {
#Resource(name="mailSender")
private JavaMailSender mailSender;
#Resource(name="mailSettings")
private SimpleMailMessage simpleMailMessage;
#Async
#Override
public void sendMailWithFileAttachment(String from, String to, String attachFile) {
logger.info("Start execution of async. Sending email with file attachment");
MimeMessage message = mailSender.createMimeMessage();
try{
MimeMessageHelper helper = new MimeMessageHelper(message, true);
....
helper.setText(String.format(simpleMailMessage.getText(), from, to));
FileSystemResource file = new FileSystemResource(attachFile);
helper.addAttachment(file.getFilename(), file);
mailSender.send(message);
} catch (MessagingException e) {
logger.error("Exception occurs in sending email with file attachment: " + attachFile, e);
throw new MailParseException(e);
}
logger.info("Complete execution of async. Email with file attachment " + attachFile + " send successfully.");
}
}
When I run the code it comes to method. This is printed on the console
13:59:43.004 [main] INFO com.softech.vu360.autoalert.service.impl.AutoAlertServiceImpl - Finish process
13:59:43.005 [SimpleAsyncTaskExecutor-1] INFO com.softech.vu360.autoalert.service.impl.AsyncEmailServiceImpl - Start execution of async. Sending email with file attachment
13:59:43.007 [main] INFO com.softech.vu360.autoalert.AutoAlert - Exiting application.
But I get no email. In case of Synchronous calling I get the email. Why I am not getting email ? Am I doing something wrong ?
Thanks
I think this is better approach. Create a file AsyncConfig.java
#Configuration
#EnableAsync(proxyTargetClass = true)
#EnableScheduling
public class AsyncConfig implements SchedulingConfigurer, AsyncConfigurer {
private static final Logger log = LogManager.getLogger();
private static final Logger schedulingLogger = LogManager.getLogger(log.getName() + ".[scheduling]");
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
log.info("Setting up thread pool task scheduler with 20 threads.");
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(2);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(1200); // 20 minutes
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setErrorHandler(t -> schedulingLogger.error("Unknown error occurred while executing task.", t));
scheduler.setRejectedExecutionHandler((r, e) -> schedulingLogger.error("Execution of task {} was rejected for unknown reasons.", r));
return scheduler;
}
#Override
public Executor getAsyncExecutor() {
Executor executor = this.taskScheduler();
log.info("Configuring asynchronous method executor {}.", executor);
return executor;
}
#Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
TaskScheduler scheduler = this.taskScheduler();
log.info("Configuring scheduled method executor {}.", scheduler);
registrar.setTaskScheduler(scheduler);
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
Now import it into your main configuration like this
#Configuration
#PropertySource(value = {
"classpath:autoalert.properties"
})
#Import({AsyncConfig.class, PersistenceConfig.class, EmailConfig.class, VelocityConfig.class})
#ComponentScan(basePackageClasses = {
ServiceMarker.class,
RepositoryMarker.class }
)
public class AutoAlertAppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Change return type from void to Future
#Service
public class AsyncEmailServiceImpl implements AsyncEmailService {
#Resource(name="mailSender")
private JavaMailSender mailSender;
#Resource(name="mailSettings")
private SimpleMailMessage simpleMailMessage;
#Async
#Override
public Future<String> sendMailWithFileAttachment(String from, String to, String attachFile) {
....
return new AsyncResult<String>("Attachment File successfully send: " + attachFile);
}
#Async
#Override
public Future<String> sendMail(String from, String to, String emailBody) {
....
return new AsyncResult<String>("Email send successfully");
}
}
and in my service class just do this
logger.info("Start process");
try {
....
//Sending email
Future<String> result = asyncEmailService.sendMailWithFileAttachment(fromDate, toDate, file);
} catch (Exception e) {
handleException(fromDate, toDate, e);
}
logger.info("Finish process");
See no need to check result.get(). Now when new Thread starts and application starts to finish. I configured the scheduler.setAwaitTerminationSeconds(1200); // 20 minutes in AsyncConfig.java. This will ensure that all the pending threads must complete before the application shuts down. Ofcourse this can be change according to any ones need.
Now when I run the application it prints these on the console
12:55:33.879 [main] INFO com.softech.vu360.autoalert.service.impl.AutoAlertServiceImpl - Finish process
12:55:33.895 [task-1] INFO com.softech.vu360.autoalert.service.impl.AsyncEmailServiceImpl - Start execution of async. Sending email with file attachment
12:58:09.030 [task-1] INFO com.softech.vu360.autoalert.service.impl.AsyncEmailServiceImpl - Complete execution of async. Email with file attachment D:\projects\AutoAlerts\marketo\autoalert 2015-08-24 to 2015-08-30.csv send successfully.
12:58:09.033 [main] INFO com.softech.vu360.autoalert.AutoAlert - Exiting application.
See starts new thread, but before application shuts down make sure, the thread completes and then exits the application. I configured 20 minutes for email, but as soon as thread completes, application get shut down. This is happy ending :)

Resources