I'm using the below snippet in Spring to schedule job executions. A job is found by querying the database. Once a new job is found (which has not yet been scheduled), it is scheduled programmatically.
My question is, is this the way to create Runnables for task execution? Is it accessing taskScheduler in the right way or should it access it by fetching an instance from application context?
#Service
public class TimeOfDayScheduler {
#Autowired
private JobExecutor jobExecutor;
#Autowired
private TaskScheduler taskScheduler;
#Scheduled(cron = "${scheduler.interval}")
#PostConstruct
public void findNewJobs() {
// Checks the database if any new jobs should be scheduled
// ...
// If found:
scheduleJob(somejob);
// ...
}
public void scheduleJob(final Job job) {
Runnable task = new Runnable() {
#Override
public void run() {
try {
jobExecutor.execute(job);
} catch (Exception e) {
logger.error("Scheduler error", e);
}
}
};
taskScheduler.schedule(task, new CronTrigger(job.getCronPattern())
}
}
Related
Faced the fact that when the database is unavailable, the queue grows because tasks stop running. What is the best way to set some timeout for tasks executed in method run()? May be there is some good approach with using ExecutorService?
#Service
public class AsyncWriter implements Writer, Runnable {
private LinkedBlockingQueue<Entry> queue = new LinkedBlockingQueue<>();
private volatile boolean terminate = false;
private AtomicInteger completedCounter = new AtomicInteger();
#PostConstruct
private void runAsyncWriter() {
Thread async = new Thread(this);
async.setName("Writer Thread");
async.setPriority(2);
async.start();
}
#Override
public void run() {
while (!terminate) {
try {
Entry entry = queue.take();
dao.save(entry);
completedCounter.incrementAndGet();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void write(Entry entry) {
queue.add(entry);
}
}
Maybe you can try RxJava
https://www.baeldung.com/rx-java
And you can set your aync funtions
Timeout in RxJava
Below the code which is executed through the cron and from this class, I'm calling my configuration class for manipulating the messages. while manipulating the message I want to interrupt this job through the rest API's.
public class CronJob extends QuartzJobBean implements InterruptableJob
{
private volatile boolean toStopFlag = true;
#Autowired
#Lazy
SchedulerFactoryBean schedulerFactoryBean;
#Autowired
MessageConfiguration messageConfiguration;
#Override
public void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
messageConfiguration.excuteMessageConfiguration(jobExecutionContext,
scheduler);
}
#Override
public void interrupt() throws UnableToInterruptJobException
{
System.out.println("Stopping thread... ");
toStopFlag = false;
}
}
************** Trying to interrupt the job*******
Here I'm trying to interrupt the job through the Rest API. Passing a job name statically.
public void interruptJob() {
List<JobExecutionContext> currentlyExecuting = scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobExecutionContext : currentlyExecuting) {
if(jobExecutionContext.getJobDetail().getKey().getName().equals("**JobName**")){
Object result = scheduler.interrupt(jobExecutionContext.getJobDetail().getKey());
System.out.println("stoppeed");
}
}
}
The issue is interrupt method is executed but the job is running continuously.
Helping hand will be appreciated.
Can a Quartz Scheduler execute a Runnable?
For example, I have the following code being running by a spring TaskScheduler:
[...]
#Autowired
#Qualifier(IntegrationConfiguration.TASK_SCHEDULER_INTEGRATION_NAME)
private TaskScheduler taskScheduler;
[...]
ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(new Runnable() {
#Override
public void run() {
try {
execucaoJobService.executa(jobName, execucaoJobDto, jobScheduleId);
} catch (JobExecutionException e) {
LOG.error("Job Execution fails", e);
}
}
}, new CronTrigger(cronExpression));
[...]
I wanna do something like the above code with Quartz, I know there is QuartzJobBean class, but this one
only works with static code, and I need to pass the cronExpression and other params dynamic.
You could define a job that takes a Runnable via the JobDataMap, and run that on execution.
The job would look like this:
public final class RunnableJob implements Job {
public static final String RUNNABLE_KEY = "RUNNABLE_KEY";
public RunnableJob() {
// Explicit constructor is required by Quartz.
}
#Override
public void execute(JobExecutionContext jobExecutionContext) {
final var runnable = (Runnable) jobExecutionContext.getJobDetail().getJobDataMap().get(RUNNABLE_KEY);
runnable.run();
}
}
Where you schedule your job it would look something like this:
final var cronTrigger = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
final var jobDetail = JobBuilder.newJob(RunnableJob.class)
.setJobData(new JobDataMap(Map.of(RunnableJob.RUNNABLE_KEY,
(Runnable) () -> {
// Do whatever you want to do
})))
.build();
scheduler.scheduleJob(jobDetail, cronTrigger);
I found this code: QuartzScheduledExecutorService.java that helps me with this problem. maybe it can help someone else in the future.
I'm using spring with RestTemplate to send POST requests to a webserver.
When my application is shut down (eg undeployed from tomcat), the shutdown should be delayed until all pending responses are received (within a timeout).
The restTemplate uses HttpComponentsClientHttpRequestFactory under the hood.
Question: how can I tell spring to delay the shutdown? #PreDestroy could be one possibility, but how can I detect pending requests on the restTemplate?
I think there is no out of the box solution as stated in https://github.com/spring-projects/spring-boot/issues/4657
For Tomcat code below should work
#Component
#Scope("singleton")
public class ApplicationContextClosedListener implements ApplicationListener<ContextClosedEvent>, TomcatConnectorCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextClosedListener.class);
private volatile Connector connector;
#Value("${timeout}")
private Integer timeout;
#Override
public void customize(Connector connector) {
this.connector = connector;
}
#Override
public void onApplicationEvent(ContextClosedEvent event) {
if (connector != null) {
shutdownGracefully();
}
}
private void shutdownGracefully() {
connector.pause();
Executor executor = connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
try {
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(timeout, TimeUnit.SECONDS)) {
LOGGER.warn("Shutdown: Tomcat thread pool did not shut down gracefully within specified period. Proceeding with forceful shutdown");
}
threadPoolExecutor.shutdownNow();
LOGGER.info("Shutdown: the executor shutdown completed");
} catch (InterruptedException ex) {
LOGGER.error("Shutdown: Interrupt signal received");
threadPoolExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}
You can execute all the requests with ExecutorService and then add a #PreDestroy hook to wait for all tasks to be completed within a given timeout. Your service can be like this
#Slf4j
#Component
public class SenderService {
private static final int TWO_SECONDS = 2;
private RestTemplate restTemplate;
private ExecutorService executorService;
public SenderService() {
this.restTemplate = new RestTemplate();
this.executorService = Executors.newFixedThreadPool(1);
}
public void sendRequest() throws Exception {
executorService.submit(() -> {
ZonedDateTime now = ZonedDateTime.now();
log.info("Sending request at {} ...", now);
restTemplate.getForObject("https://httpbin.org/delay/{delay}", Void.class, TWO_SECONDS, now);
log.info("Response received for request at {}", now);
return null;
}).get();
}
#PreDestroy
public void destroy() throws InterruptedException {
log.info("Shutting down sender service...");
executorService.shutdown();
executorService.awaitTermination(3, TimeUnit.SECONDS);
log.info("Sender service terminated.");
}
}
The simple way to test this is running the application below and terminating it at some point.
#SpringBootApplication
public class Application {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
SenderService senderService = run.getBean(SenderService.class);
while (true) {
senderService.sendRequest();
}
}
}
If you gracefully shut down the application, you'll see that if a request is sent to delay endpoint, the executorService is going to wait up to 3 seconds for the task to be completed and then terminate the component. executorService.shutdown() initiates a shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
This code is using spring-boot with embedded tomcat, but the same approach could be applied to any spring application context.
I am using spring boot and have one async method. To execute async I have below configuration, questions is what if all those 5 thread hangs for some reason , essentially it will lock the application and none of new task will be executed (it will just keep accepting). How we can set timeout for those working thread , lets say 120 seconds, so after that it timesout and execute new task. (Yes I am using fixed thread pool with unbounded queue to keep accepting tasks)
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(5);
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
You can create another executor like:
static class TimeOutExecutorService extends CompletableExecutors.DelegatingCompletableExecutorService {
private final Duration timeout;
private final ScheduledExecutorService schedulerExecutor;
TimeOutExecutorService(ExecutorService delegate, Duration timeout) {
super(delegate);
this.timeout = timeout;
schedulerExecutor = Executors.newScheduledThreadPool(1);
}
#Override public <T> CompletableFuture<T> submit(Callable<T> task) {
CompletableFuture<T> cf = new CompletableFuture<>();
Future<?> future = delegate.submit(() -> {
try {
cf.complete(task.call());
} catch (CancellationException e) {
cf.cancel(true);
} catch (Throwable ex) {
cf.completeExceptionally(ex);
}
});
schedulerExecutor.schedule(() -> {
if (!cf.isDone()) {
cf.completeExceptionally(new TimeoutException("Timeout after " + timeout));
future.cancel(true);
}
}, timeout.toMillis(), TimeUnit.MILLISECONDS);
return cf;
}
}
Then, create a new bean named timed
#Bean(name = "timed")
public Executor timeoutExecutor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("timed-%d").build();
return TimedCompletables.timed(Executors.newFixedThreadPool(10, threadFactory), Duration.ofSeconds(2));
}
And, try to use this Executor to execute your async tasks.
Or, try to change your code from FixSizeThreadPool to build a own thread pool executor.
You can not submit some task with timeout. What you can do is when you submit your task your get a Future object. You can keep this reference in some Map and pole and see if the task keeps running past your timeout. If so you can use the method cancel() of class Future.
Alternatively, your own task when it starts running places its own current thread into some Map visible to your main (submitting) thread. Also if you see that your task didn't finish in time (again poling) you can try and interrupt your thread. In either case your submitted task should be able to react to interrupt() method of Thread class. I actually implemented this alternative way. If you go this way, test a LOT... :)
I think Future.get(timeout, unit) method can manage async timeout.
Following example can work on my local.
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class AsyncTimeoutExampleAppliation {
private final MyService myService;
public AsyncTimeoutExampleAppliation(MyService myService) {
this.myService = myService;
}
public static void main(String[] args) {
SpringApplication.run(AsyncTimeoutExampleAppliation.class, args);
}
#PostConstruct
void postConstract(){
asyncCall();
}
public void asyncCall(){
try {
String result = myService.doSomething()
.get(10, TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
#Service
public static class MyService {
#Async
public Future<String> doSomething() throws InterruptedException {
TimeUnit.SECONDS.sleep(60);
return CompletableFuture.completedFuture("Finished");
}
}
}
We can get TimeoutException 10 seconds after application starts.