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.
Related
I have got a requirement to get the list of all the scenarios that are to be executed based on the tag I provided in cucumber Test runner. However I have to get this list before tests start execution.
I know there is a tag called "#BeforeClass" but I am not sure if I can use to get the list of all the scenarios that are going to be run. For example something like this
#BeforeClass
public void intialize(Scenario[] scenario) throws Exception { }
Below is the code for me test runner class
package com.automation.cucumber;
import com.automation.Utils;
import io.cucumber.java.Scenario;
import io.cucumber.testng.*;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import java.io.File;
#CucumberOptions(features = "features/amazon"
,glue="com.automation.cucumber"
,tags = "#tt"
,dryRun = true
, plugin = {"json:target/cucumber-reports/cucumber.json"})
public class CucumberTestRunner extends AbstractTestNGCucumberTests {
static String resultFolder;
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
#BeforeClass
public void intialize() throws Exception {
resultFolder = Utils.createTestReportFolder();
if(resultFolder==null)
{
throw new Exception("Unable to create a result folder");
}
System.out.println();
}
}
You may have to implement EventListener class to get that information and do dryRun = true in your Runner class in #CucumberOptions
Quoting from a question that can help you achieve what you need
public class DryRunPlugin implements EventListener {
#Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestCaseStarted.class, this::handleCaseStarted);
}
private void handleCaseStarted(TestCaseStarted event) {
System.out.println(event.getTestCase().getUri());
System.out.println(event.getTestCase().getName());
System.out.println(event.getTestCase().getScenarioDesignation());
event.getTestCase().getTags().stream().forEach(t ->
System.out.println(t.getName()));
}
}
I have a random class in a random package that is loaded through reflection after the app launches, is there a way for it to be registered as a component under springboot and have annotations such as #Autowired and #Value etc work for that class.
It works when it is in the same package at launch time, but if introduce it thorough another jar at runtime (same package or not) it doesn't work.
Below are samples that don't work even if it is in the same jar. I can't change the app's configuration - it would defeat the "random package/random class" objective.
Code in Spring boot application package
package sample.app
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// Code that starts app
//
//
try {
Thread.sleep(7000);
Class test = Class.forName("test.Test", true, Application.class.getClassLoader());
System.out.println(test.getMethod("getName").invoke(null)); //NPE
System.out.println(test.getMethod("getProfiles").invoke(null)); //NPE
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Test.java
package test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.DependsOn;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
#DependsOn("blaaaaaaaah")
#ComponentScan
public class Test {
#DependsOn("blaaaaaaaah")
public static String getName() {
return SpringGetter.instance.getApplicationName();
}
#DependsOn("blaaaaaaaah")
public static String[] getProfiles() {
String[] profiles = SpringGetter.instance.getEnv().getActiveProfiles();
if (profiles == null || profiles.length == 0) {
profiles = SpringGetter.instance.getEnv().getDefaultProfiles();
}
return profiles;
}
}
SpringGetter.java
package test;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component("blaaaaaaaah")
public class SpringGetter implements InitializingBean {
public static SpringGetter instance;
#Value("${spring.application.name}")
private String applicationName;
#Autowired
private Environment env;
public SpringGetter() {
System.out.println("consASFJEFWEFJWDNFWJVNJSBVJWNCJWBVJNVJNVJSNJSNCSDJVNSVJtruct");
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
#PostConstruct
public void setInstance() {
instance = this;
}
#Override
public void afterPropertiesSet() throws Exception {
instance = this;
}
}
EDIT:
I managed to dynamically create the SpringGetter class as part of the same package as the Application class(the one with the #SpringBootApplication). I got Test.java to point to that dynamic class and yet no luck.
To simply inject fields into a POJO as if it were a Spring-managed bean, you can use something like the following:
#Component
public class BeanInitializer implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
public void initializeObject(Object pojo) {
beanFactory.autowireBean(pojo);
}
}
Note, however, that this only injects fields marked as #Autowired or #Injected. It does not create proxies that honor method interception strategies based on e.g. #Transactional, #Async, etc.
If you're using Spring 5, have a look at the registerBean() method from GenericApplicationContext. You can find an example here: https://www.baeldung.com/spring-5-functional-beans
The issue in your Test class may also be that you're not loading the Spring Boot context from the main class. You can use the SpringBootTest annotation for this.
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 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();
}
}
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...
}
}
}