I am attempting to create a SpringBoot application that will consume data from a 3rd party REST API and push Websocket notifications to my own clients based on events/changes to that data. The data I am consuming changes frequently, sometimes dozens of times a second (crypto currency price fluctuations behave similarly to this data). I want to repeatedly call the API on a fixed interval (every 1-10 seconds for example), watch for certain events/changes and trigger a Websocket push when those events occur.
I've been able to build a simple Spring Boot app that can push Websocket Notifications and consume the API by following these guides:
Spring.IO Websockets
Spring.IO Consuming REST
The Problem: I can only get the Application to request the data from the API once. I've spent hours searching every variation of "Spring RestTemplate multiple/repeated/persistent calls" I can think of, but I cannot find a solution that addresses my specific use. The closest examples I've found use retries but even those will eventually give up. I want my application to continually request this data until I shut the application down. I know I could wrap it in a while(true) statement or something like that, but that really doesn't seem right for a framework like SpringBoot and still has problems when trying to instantiate the RestTemplate.
How can I implement persistent querying of a RESTful API resource?
Below is what I have in my Application class
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.client.RestTemplate;
#SpringBootApplication
#EnableScheduling
public class Application {
private static final String API_URL = "http://hostname.com/api/v1/endpoint";
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
#Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
Response response= restTemplate.getForObject(API_URL, Response.class);
System.out.println(response.toString());
};
}
}
CommandLineRunner only runs once per application start. Instead, you want to use the #Scheduled annotation to perform repeated operations at fixed intervals like
#Scheduled(fixedDelay = 1000L)
public void checkApi() {
Response response = restTemplate.getForObject(API_URL, Response.class);
System.out.println(response.toString())
}
It does not need to be a Bean, it can just be a simple method. See the Spring guide for more information https://spring.io/guides/gs/scheduling-tasks/
Add #EnableScheduling annotation on your SpringConfig class or Main class.
You can use Scheduled fixedDelay OR fixedRate
#Scheduled(fixedDelay = 10000)
public void test() {
System.out.println("Scheduler called.");
}
OR
#Scheduled(fixedRate = 10000)
public void test() {
System.out.println("Scheduler called.");
}
Difference between fixedDelay and fixedRate:
fixedDelay - makes sure that there is a delay of n millisecond between the finish time of an execution of a task and the start time of the next execution of the task.
fixedRate - runs the scheduled task at every n milliseconds.
Ideally, you should externalise the fixedDelay or fixedRate value as well in application.properties file:
#Scheduled(fixedDelayString = "${scheduler.fixed.delay}")
public void test() {
System.out.println("Scheduler called.");
}
In your application.properties file add below config:
scheduler.fixed.delay = 10000
Hope this helps.
Related
I have one microservice in which I have one rest controller and one rabbitMQ receiver, sometimes when I start the server receiver and API both are working fine but after some time receiver is not working it stops receiving new messages from the queue.
This is my rabbit configuration bean class:
#Configuration
public class RabbitMQConfiguration {
#Bean
public Queue claimQueue() {
return new Queue("queue");
}
}
other credentials-related configurations are in the application.properties file. And this is my receiver part
#RabbitListener(queues = "queue")
public class ClaimValidatorReciever {
#RabbitHandler
public void reciever(RabbitMQValidatorModel validatorModel)
throws InterruptedException, ExecutionException { }
Is it possible to have both in one microservice, and if possible any idea about this issue?
I tried to restart my server when this thing happen but after restarting this is working but after some time again facing this same issue of not fetching data from queue
I have several applications monitored under Spring Boot Admin. Spring Boot Admin is great at telling me if an application is up or down and other various metrics.
I would also like to know that certain URLs exposed by these applications are returning an HTTP status of 200. Specifically, I would like to send a GET request to these URLs once a day. If it receives a non 200 status from any of these, it sends an email stating which URLs are reporting non 200.
Is something that Spring Boot Admin is able to do? I know about custom HealthIndicators but not sure if it can be scheduled or if it's appropriate for this.
Just wanted to see if there is something Spring Boot Admin offers to support doing this before I build my own app to make the GET calls and send the email.
Update
The URLs are exposed as Eureka services and I'm calling services from other services via Spring Cloud OpenFeign.
Update 2
I went ahead and built my own custom application to handle this. Details follow but still interested if Spring offers something out-of-the-box to do this.
application.yml
app:
serviceUrls:
- "http://car-service/cars?category=sedan"
- "http://truck-service/trucks"
cron: "0 0 10 * * *"
Urls are read into:
#Component
#ConfigurationProperties(prefix = "app")
#Getter
#Setter
public class ServiceUrls {
private String[] serviceUrls;
}
Via cron, scheduled to run once a day:
#Component
#RequiredArgsConstructor
#Slf4j
public class ServiceCheckRunner {
private final ServiceHealth serviceHealth;
#Scheduled(cron = "${cron}")
public void runCheck() {
serviceHealth.check();
}
}
This is the code that checks whether URLs return no errors:
#Service
#RequiredArgsConstructor
#Slf4j
public class ServiceHealth {
private final ServiceUrls serviceUrls;
private final RestTemplate rest;
public void check() {
List<String> failedServiceUrls = new ArrayList<>();
for (String serviceUrl : serviceUrls.getServiceUrls()) {
try {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
} catch (Exception e){
failedServiceUrls.add(serviceUrl);
}
}
// code to send an email with failedServiceUrls.
}
}
You can use Spring Boot Admin in order to send email notifications whenever a registered client changes his status from UP to OFFLINE or otherwise.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.4.0</version>
</dependency>
application.properties
spring.mail.host=smtp.example.com
spring.mail.username=smtp_user
spring.mail.password=smtp_password
spring.boot.admin.notify.mail.to=admin#example.com
But, if you really need to check client status once per day, you need to implement a custom solution.
In my Spring boot controller, I am having a method that inserts some records to the backend, at the end of this operation, I notify the user via Javax email based on the response received from from previous operation.
Currently I get response from API after email method completes.
Is there any way I can return response to the client once my first operation is completed while email notification happens in background
I tried already implement Async annotation in sendemail method of mail service. But I cannot find any difference in response time and I still get the response only after the email is sent.
My pseudo code
Controller:
#Autowired
private EmailService emailService;
#PostMapping(value = "create", produces = "text/plain")
private insertRecord()
{
response = <Insert into DB>;
sendEmail(response);
}
private sendEmail(response)
{
//check if email should be sent and if yes
emailservice.send(response);
}
Email service:
#Service
public class EmailService {
#Async
public static void sendEmail(MailEvent mailEvent) throws IOException {//send
email}
}
Starter
#SpringBootApplication
#EnableAsync
public class Starter {...}
i didn't notice my async method was designed as static, removing static works for me.
I think Spring Async is the way to go to solve your problem. Did you you also enable the async functionality with #EnableAsync and create an Executor bean? See this guide for the full tutorial: https://spring.io/guides/gs/async-method/
I'm trying to understand how to publish/broadcast messages using websockets with Spring Boot to a Javascript application. All examples I can find are making use of a StompJs client - I however am unable to use StompJs in my client code, and I'm not sure my backend is correct which doesn't help.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/subscribe")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
Just using a simple #Scheduled to produce the time every 5 seconds, and send it to the time topic (Well, I believe that's what it's doing...)
#Component
#Slf4j
public class TimeSender {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
private SimpMessagingTemplate broker;
#Autowired
public TimeSender(final SimpMessagingTemplate broker) {
this.broker = broker;
}
#Scheduled(fixedRate = 5000)
public void run() {
String time = LocalTime.now().format(TIME_FORMAT);
log.info("Time broadcast: {}", time);
broker.convertAndSend("/topic/time", "Current time is " + time);
}
}
There are a few points I'm a little confused about when trying to test this. Using the Simple websocket client plugin for Chrome, I have to add websocket to the end of my request in order to connect. A connection would like ws://localhost:8080/subscribe/websocket Without the websocket I can't connect, but I can't find this mentioned in any examples or Spring documentation?
The second question is how do I subscribe to the time topic? All StompJs clients call something like client.subscribe("time") etc.
I've tried ws://localhost:8080/subscribe/topic/time/websocket but no luck in receiving any timestamps.
I'm not sure if my backend code is just wrong, my URL is wrong, or I'm just missing something else.
Note: My #Controller is missing from above as I'm just focused on pushing messages from Spring to clients at this stage, not receiving messages and It's my understanding controllers just deal with incoming?
Well, I suppose if one searches obsessively enough the answer eventually turns up. Almost immediately after finding your post I found the answer I needed at http://www.marcelustrojahn.com/2016/08/spring-boot-websocket-example/. There is a really good example that essentially does what you are describing. The difference is they are using a Spring SimpMessagingTemplate to send messages to the queue. Once I followed his pattern, it all worked like a charm. Here is the relevant code snippet:
#Autowired
SimpMessagingTemplate template
#Scheduled(fixedDelay = 20000L)
#SendTo("/topic/pingpong")
public void sendPong() {
template.convertAndSend("/topic/pingpong", "pong (periodic)")
}
The method is void so the convertAndSend() method handles publishing to the topic, not the return statement as just about every other tutorial I've seen on the web indicates. This helped solve my problem.
I am developing server side web services code. I am using JAX-RS as development framework.
So far I have created model classes and resources class that responds requested data to client.
Sample resource method...
#GET
#Path("/{userId}")
#Produces(MediaType.APPLICATION_JSON)
public User getUserDetails(#PathParam("userId") long id) {
..
// some code here //
..
}
Basically, server responds the data or do some operations depends on the URI is been called by the client.
I want to make Http POST request to third-party server at every two minutes from the moment server starts. But I dont know where should I write that code (as I said, methods executions depends on the URI is been called).
So, where should I write the code that starts executing when the server starts and ends when server stops.
How to send Http request at every two minutes interval ?
You should be able to do that with a combination of Quartz and ServletContextListener.
You will need to create a job, trigger and scheduler to make your code run after every two minutes and a listener class that implements ServletContextListener interface.
Your code would look something like this:
Job Class:
package com.example;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class ExampleJob implements Job
{
public void execute(JobExecutionContext context) throws JobExecutionException {
// Code to make POST call here
}
ServletContextListener
package com.example;
public class ExampleListener implements javax.servlet.ServletContextListener {
public void contextInitialized(ServletContext context) {
JobDetail job = JobBuilder.newJob(ExampleJob.class)
.withIdentity("exampleJob", "group").build();
// Trigger
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("exampleTrigger", "group")
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(120).repeatForever())
.build();
// Scheduler
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
}
And add this in web.xml:
<listener>
<listener-class>com.example.ExampleListener</listener-class>
</listener>
or if you are using servlet container 3.x, you can skip the web.xml modification by annotating the listener class with #WebListener