I have this simple example of Spring Scheduler:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class AppScheduler {
#Scheduled(fixedRate = 10000)
public void myScheduler() {
System.out.println("Test print");
}
}
Is there a way to trigger execution in the current moment let's say from web page?
Just make a dump controller to call myScheduler method:
#Controller
public class DumpController {
#Autowired
private AppScheduler scheduler;
#RequestMapping("/ping")
public void ping() {
scheduler.myScheduler();
}
}
Related
I'm just trying use Camel Reactive Stream together with Spring Boot Reactor using the following code
package com.manning.camel.reactive;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.reactive.streams.api.CamelReactiveStreamsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
/**
* A simple Camel route that triggers from a timer and calls a bean and prints to system out.
* <p/>
* Use <tt>#Component</tt> to make Camel auto-detect this route when starting.
*/
#RestController
public class MySpringBootRouter extends RouteBuilder {
#Autowired
private ProducerTemplate template;
#Autowired
private CamelReactiveStreamsService crss;
#GetMapping
public Mono<String> sayHi() {
template.asyncSendBody("direct:works", "Hi");
//return Mono.from(crss.fromStream("greet", String.class));
return Mono.from(crss.fromStream("greet", String.class));
}
#Override
public void configure() {
from("direct:works")
.log("Fired")
.to("reactive-streams:greet");
}
}
After run the code
java.lang.IllegalStateException: The stream has no active subscriptions
After a long time, solved the error, as can be noticed the Router Class logic was changed a little
#Slf4j
#Service
#AllArgsConstructor
public class MyService {
final CamelContext context;
#PostConstruct
public void consumerData() {
var rCamel = CamelReactiveStreams.get(context);
var numbers = rCamel.fromStream("numbers", Integer.class);
Flux.from(numbers).subscribe(e -> log.info("{}", e));
}
}
#Component
#NoArgsConstructor
public class MyRouter extends RouteBuilder {
// Injects the Subscriber
#Autowired MyService service;
#Override
public void configure() {
//onException(ReactiveStreamsNoActiveSubscriptionsException.class)
// .continued(true);
from("timer://reactiveApp?fixedRate=true&period=2s")
.transform(method(Random.class, "nextInt(100)"))
//.log("${body}");
.to("direct:message");
from("direct:message")
//.log("${body}")
.to("reactive-streams:numbers");
}
}
I'm trying to run a method in Spring with ScheduledTasks, so I have the following class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.time.format.DateTimeFormatter;
#Component
public class ScheduledTasks {
private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
public void scheduleTaskWithFixedRate() {
}
public void scheduleTaskWithFixedDelay() {
}
public void scheduleTaskWithInitialDelay() {
}
public void scheduleTaskWithCronExpression() {
}
}
And the following method in a different class
#Scheduled(fixedRate = 10 * 1000) //10 seconds
public void taskThatRunsPeridically() {
logger.info("Scheduled task method has been called ");
}
But the method never runs, I've noticed thought that if I move the method to the Spring Boot Application class (the class that hosts main)
Why is this happening? How I can get schedule methods to run in wherever class that I add them?
You have to add the #EnableScheduling annotation in one of your Spring configuration classes or above the other class that contains your method, for example:
#Component
#EnableScheduling
public MySchdeduleClass {
#Scheduled(fixedRate = 10 * 1000) //10 seconds
public void taskThatRunsPeridically() {
logger.info("Scheduled task method has been called ");
}
}
I created a simple aspect in Spring using Spring Boot 2.1.6.RELEASE.
It basically logs the total time spent on a method.
#Aspect
#Component
public class TimeLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(TimeLoggerAspect.class);
#Around("#annotation(demo.TimeLogger)")
public Object methodTimeLogger(ProceedingJoinPoint joinPoint)
throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");
return proceed;
}
}
the aspect is triggered by a TimeLogger annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TimeLogger {
}
and is used in a component like this
#Component
public class DemoComponent {
#TimeLogger
public void sayHello() {
System.out.println("hello");
}
}
A spring boot demo application will invoke sayHello via the run method of the CommandLineRunner interface.
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Autowired
private DemoComponent demoComponent;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
demoComponent.sayHello();
}
}
For completeness, I add my modifications in build.gradle: adding libraries for aop, spring test and jupiter (junit).
compile("org.springframework.boot:spring-boot-starter-aop")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("org.junit.jupiter:junit-jupiter-api")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
Running the application will output (trimmed for readability)
hello
... TimeLoggerAspect : Method void demo.DemoComponent.sayHello(): 4ms
So far, so good. Now I create a test based on #SpringBootTest annotation and jupiter.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {DemoComponent.class, TimeLoggerAspect.class})
public class DemoComponentFailTest {
#Autowired
private DemoComponent demoComponent;
#Test
public void shouldLogMethodTiming() {
demoComponent.sayHello();
}
}
and here I get the output
hello
No output from the TimeLoggerAspect, since it seems it is not being triggered.
Is something missing to trigger the aspect in the test? Or are there other ways of testing the aspect in spring boot?
I had similar problem. My Aspect is listening on controller methods. To get it activated, importing the AnnotationAwareAspectJAutoProxyCreator made the trick:
#RunWith(SpringRunner.class)
#Import(AnnotationAwareAspectJAutoProxyCreator.class) // activate aspect
#WebMvcTest(MyController.class)
public class MyControllerTest {
...
}
You have to put #EnableAspectJAutoProxy with your file #Configuration that declares the bean with #Aspect.
#Aspect
#Configuration
#EnableAspectJAutoProxy
public class TimeLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(TimeLoggerAspect.class);
#Around("#annotation(demo.TimeLogger)")
public Object methodTimeLogger(ProceedingJoinPoint joinPoint)
throws Throwable {
long startTime = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long totalTime = System.currentTimeMillis() - startTime;
log.info("Method " + joinPoint.getSignature() + ": " + totalTime + "ms");
return proceed;
}
}
I think that will do the work.
You need to start an #SpringBootApplication. However, it does not have to be the one you use to start your app in production. It can be a special one for this test only and can be in your test sources root not your src.
#SpringBootApplication
#ComponentScan(basePackageClasses = {DemoComponent.class, TimeLoggerAspect.class})
public class SpringBootTestMain {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestMain.class, args);
}
}
Then in your test this is the only class you need to list.
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = SpringBootTestMain.class)
public class DemoComponentFailTest {
Another solution that seems to work is adding AnnotationAwareAspectJAutoProxyCreator in classes of #SpringBootTest, although I am not quite certain why.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = { DemoComponent.class,
TimeLoggerAspect.class,
AnnotationAwareAspectJAutoProxyCreator.class })
public class DemoComponentFailTest {
#Autowired
private DemoComponent demoComponent;
#Test
public void shouldLogMethodTiming() {
demoComponent.sayHello();
}
}
When I had to test an aspect, I used the approach below.
#SpringBootTest
#ContextConfiguration(classes = {MyAspectImpl.class, MyAspectTest.TestConfiguration.class})
#EnableAspectJAutoProxy
public class MyAspectTest {
#org.springframework.boot.test.context.TestConfiguration
static class TestConfiguration {
#Bean
public MyAspectTestClass myAspectTestClass() {
return new MyAspectTestClass();
}
}
#Autowired
private MyAspectTestClass target;
#Test
public void testCorrectlySetsPoolNameUsingMethodParameter() {
target.testMethod();
}
#NoArgsConstructor
private static class MyAspectTestClass {
#MyAspect
public void testMethod() {
//Add some logic here
}
}
}
tl;dr: Can you think of any case I could look for where #Scheduled tasks are not executed in a Spring Boot app?
I implemented the example and it worked just fine, however, in a more complex Spring Boot app I am working on I can't get the #Scheduled methods to run.
My main class looks like this:
package com.mypackage.myapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
#SpringBootApplication
#EnableWebSecurity
#EnableScheduling
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class);
}
}
and the component in which the scheduled app lives looks like this:
(...)
#Component
public class MyComponent {
private static final Logger logger = LoggerFactory.getLogger(MyComponent.class);
(...)
#Scheduled(fixedRate = 5000)
public void myScheduledTask() {
logger.info("Executing scheduled task");
(...)
}
}
The whole application is obviously more complex, but this is essentially it. Unfortunately, "Executing scheduled task" appears no where in the logs, also if I debug the breakpoint is never reached.
As I said, the minimal example works for me, however in my app it isn't. Can you think of any case I could check for where #Scheduled tasks are not executed? For example, can the configuration be overruled by anything? Can anything interfere?
I am using version 1.5.1.RELEASE.
I think I figured it out. I have another component that implements SmartLifecycle, and the start() method contains a while loop that I never get out of (intentionally, reading from a Kafka stream). This essentially leads to the initialization being stuck, and hence the #Scheduled method never actually being scheduled.
I could just reproduce that in a minimal example with a while(true). As soon as I have that, the #Scheduled method doesn't get executed, when I remove the while, it works just fine.
package com.mypackage.myapp;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
#Component
public class StartLoop implements SmartLifecycle {
private static final Logger log = LoggerFactory.getLogger(StartLoop.class);
private static final int LAST_PHASE = Integer.MAX_VALUE;
private boolean running;
#Override
public void start() {
runMyCode();
}
private void runMyCode() {
running = true;
log.info("Starting ...");
while (running) {
try {
log.info("running");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public boolean isRunning() {
return running;
}
#Override
public void stop() {
log.info("Stopping ...");
}
#Override
public void stop(Runnable callback) {
callback.run();
}
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public int getPhase() {
return LAST_PHASE;
}
}
If I now replace the start() method by
#Override
public void start() {
CompletableFuture.runAsync(this::runMyCode);
}
it works perfectly fine.
I am building a Springboot application and I want to turn on a scheduled method from the front-end. (as in I want the scheduler to run only after the method is called from the front-end)
This scheduled method will then call a web-service with the given parameter and keep on running until a specific response ("Success") is received.
Once the specific response is received, I want the scheduled method to stop running until it is called again from the front end.
I am not sure how to start and stop the execution of the scheduled method.
I have this currently:
#Component
public class ScheduledTasks {
private static final Logger LOG = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void waitForSuccess(String componentName) {
LOG.info("Running at: " + dateFormat.format(new Date()));
String response = MyWebService.checkStatus(componentName);
if ("success".equalsIgnoreCase(response)) {
LOG.info("success");
//Stop scheduling this method
} else {
LOG.info("keep waiting");
}
}
}
Here is my controller through which the scheduled method is to be turned on:
#Controller
public class MainController {
#GetMapping(/start/{componentName})
public #ResponseBody String startExecution(#PathVariable String componentName) {
//do some other stuff
//start scheduling the scheduled method with the parameter 'componentName'
System.out.println("Waiting for response");
}
}
Is my approach correct? How can I achieve this functionality using springboot and schedulers?
Here is the full example of start/stop API for a scheduled method in Spring Boot. You can use such APIs:
http:localhost:8080/start - for starting scheduled method with fixed rate 5000 ms
http:localhost:8080/stop - for stopping scheduled method
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.time.Instant;
import java.util.concurrent.ScheduledFuture;
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class TaskSchedulingApplication {
public static void main(String[] args) {
SpringApplication.run(TaskSchedulingApplication.class, args);
}
#Bean
TaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
}
#Controller
class ScheduleController {
public static final long FIXED_RATE = 5000;
#Autowired
TaskScheduler taskScheduler;
ScheduledFuture<?> scheduledFuture;
#RequestMapping("start")
ResponseEntity<Void> start() {
scheduledFuture = taskScheduler.scheduleAtFixedRate(printHour(), FIXED_RATE);
return new ResponseEntity<Void>(HttpStatus.OK);
}
#RequestMapping("stop")
ResponseEntity<Void> stop() {
scheduledFuture.cancel(false);
return new ResponseEntity<Void>(HttpStatus.OK);
}
private Runnable printHour() {
return () -> System.out.println("Hello " + Instant.now().toEpochMilli());
}
}
Start/stop API for a scheduled method in Spring Boot.
#Component
public class ScheduledTasks {
private Logger logger = Logger.getLogger(ScheduledTasks.class);
#Value("${jobs.schedule.istime}")
private boolean imagesPurgeJobEnable;
#Override
#Transactional(readOnly=true)
#Scheduled(cron = "${jobs.schedule.time}")
public void execute() {
//Do something
//can use DAO or other autowired beans here
if(imagesPurgeJobEnable){
// Do your conditional job here...
}
}
}