Spring MVC how to get progress of running async task - spring

I would like to start an asynchronous task from within controller like in following code sniplet from Spring docs.
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private int cn;
public MessagePrinterTask() {
}
public void run() {
//dummy code
for (int i = 0; i < 10; i++) {
cn = i;
}
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
taskExecutor.execute(new MessagePrinterTask());
}
}
afterwards in annother request (in the case that task is running) I need to check the progress of the task. Basicaly get the value of cn.
What would be the best aproach in Spring MVC a how to avoid syncronisation issues.
Thanks
Pepa Procházka

Have you looked at the #Async annotation in the Spring reference doc?
First, create a bean for your asynchronous task:
#Service
public class AsyncServiceBean implements ServiceBean {
private AtomicInteger cn;
#Async
public void doSomething() {
// triggers the async task, which updates the cn status accordingly
}
public Integer getCn() {
return cn.get();
}
}
Next, call it from the controller:
#Controller
public class YourController {
private final ServiceBean bean;
#Autowired
YourController(ServiceBean bean) {
this.bean = bean;
}
#RequestMapping(value = "/trigger")
void triggerAsyncJob() {
bean.doSomething();
}
#RequestMapping(value = "/status")
#ResponseBody
Map<String, Integer> fetchStatus() {
return Collections.singletonMap("cn", bean.getCn());
}
}
Remember to configure an executor accordingly, e.g.
<task:annotation-driven executor="myExecutor"/>
<task:executor id="myExecutor" pool-size="5"/>

One solution could be: in your async thread, write to a DB, and have your checking code check the DB table for progress. You get the additional benefit of persisting performance data for later evaluation.
Also, just use the #Async annotation to kick off the asynchronous thread - makes life easier and is a Spring Way To Do It.

Check this github source, it gives pretty simple way of catching status of the background job using #Async of Spring mvc.
https://github.com/frenos/spring-mvc-async-progress/tree/master/src/main/java/de/codepotion/examples/asyncExample

Ignoring synchronization issues you could do something like this:
private class MessagePrinterTask implements Runnable {
private int cn;
public int getCN() {
return cn;
}
...
}
public class TaskExecutorExample {
MessagePrinterTask printerTask;
public void printMessages() {
printerTask = new MessagePrinterTask();
taskExecutor.execute(printerTask);
}
...
}

Related

Dependency injection of object initialised by specific value - CDI and Spring

I have a legacy application which uses Spring for bean management (AnnotationConfigWebApplicationContext) and CDI (inject, brought by jersey-spring dependency) for DI.
I have the following situation:
#Service
#RequestScoped
#Scope(value = "request")
public class InjectedClass {
private SomeEnum someAttribute;
public void getSomeAttribute() {
return someAttribute;
}
}
#Service
public class MiddleLayer {
#Inject
public MiddleLayer(InjectedClass injectedClass) {
}
private void middleLayerMethod() {
if (someAttribute == SomeEnum.Y) {
// do something specific
}
}
}
// controller
public class SomeController {
#Inject
// In this flow injectedClass instance is initialised with SomeAttribute = Y by ContainerRequestFilter
public SomeController(MiddleLayer middleLayer) {
}
public void someMethod () {
MethodResult result = middleLayer.middleLayerMethod();
// do some additional things with result
}
}
#Component
public class PeriodicActivity {
#Inject
// I need this MiddleLayer to be injected with injectedClass instance where SomeAttribute = X, since it doesn't go via request filter
public PeriodicActivity(MiddleLayer middleLayer) {
}
public void method () {
MethodResult result = middleLayer.middleLayerMethod();
// do some other things with result
}
}
Without DI what I need to happen would look like this:
public class PeriodicActivity() {
InjectedClass injectedClassObjA = new InjectedClass();
injectedClassObjA.setSomeAttribute(X);
MiddleLayer middleLayer = new MiddleLayer(injectedClassObjA);
middleLayer.middleLayerMethod();
}
I am looking to do something similar with dependencies.
After some reading I am starting to wonder whether it is possible.

Spring boot TaskExector makes API execution slower

I'm making use of TaskExecutor to run some tasks in background threads. Following is my configuration:-
#Configuration
public class TaskExecutorConfig
{
#Bean
public TaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(4);
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(false);
threadPoolTaskExecutor.setThreadNamePrefix("TaskExecutor");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
There can be many tasks to run in background. I have declared a ThreadComponent implementing Runnable class. In this ThreadComponent, I have declared methods which get executed based on condition. Following is the component and what it somewhat looks like:-
public class ThreadComponent implements Runnable
{
....
....
....
....
public ThreadComponent()
{
//................//
//Initialization code for variables and some autowired components//
//................//
}
#Override
public void run()
{
if (condition)
doThis();
else if (condition)
doThat();
else if (condition)
doThen();
else if (condition)
doWhere();
else if (condition)
doHow();
else if (condition)
doWhen();
else if (condition)
doNothing();
}
public void doThis() {
......
.....,
}
public void doThat() {
......
.....,
}
public void doThen() {
......
.....,
}
public void doWhere() {
......
.....,
}
public void doHow() {
......
.....,
}
public void doWhen() {
......
.....,
}
public void doNothing() {
......
.....,
}
}
I call this component using TaskExecutor for tasks. In ThreadComponent, I have also used #Autowired components which I initialize in the constructor using ApplicationContext. But this is making my processing slow. I need help so how can I optimize my code to make it better.
Update
Please find the class variables and constructor of the component:
private DTO dto1;
private DTO2 dto2;
private Map<String, Object> dtoVal;
private String schemaName;
private int checkCondition;
private AppUser user;
private Details oldApprovalDetails;
private Details newApprovalDetails;
private ThreadDTO threadDTO;
#Autowired
OcrService ocrService;
#Autowired
NotificationService notificationService;
#Autowired
CreateRecordsService createRecordService;
#Autowired
DtoService dtoService;
#Autowired
DtoCalculations dtoCalculations;
#Autowired
MailService mailService;
#Autowired
TransDtoRepository transDtoRepository;
#Autowired
TransDtoApproverDetailsRepository approverDetails;
public ThreadComponent(ThreadDTO threadDTO,
List<Details> approvalDetails, ApplicationContext ctx)
{
this.dto1 = threadDTO.getDto1();
this.dto2 = threadDTO.getDto2();
this.dtoVal = threadDTO.getDto1().getConfig();
this.schemaName = threadDTO.getDto1().getSchemaName();
this.checkCondition = threadDTO.getCheckCondition();
this.user = threadDTO.getUser();
this.oldApprovalDetails = approvalDetails.get(0);
this.newApprovalDetails = approvalDetails.get(1);
this.threadDTO = threadDTO;
this.ocrService = ctx.getBean(OcrService.class);
this.notificationService = ctx.getBean(NotificationService.class);
this.createRecordService = ctx.getBean(CreateRecordsService.class);
this.dtoService = ctx.getBean(DtoService.class);
this.dtoCalculations = ctx.getBean(DtoCalculations.class);
this.mailService = ctx.getBean(MailService.class);
this.transDtoRepository = ctx.getBean(TransDtoRepository.class);
this.approverDetails = ctx.getBean(TransDtoApproverDetailsRepository.class);
}
Constructor contains all the variables declared in class scope.
I implemented a work around and that helped me solve my issue. I implemented spring boot #Async annotation service and added all my backend code that doesn't require to interrupt the user in that service. This service then makes call to the TaskExecutor for multiple threads. In short, I implemented both #Async and TaskExecutor framework for one complex functionality and that made my code faster (now it takes 300 milliseconds to 500 milliseconds to give me response).
Hope this helps anyone who is in need.

Field created in spring component in not initialized with new keyword

I have spring component class annotated with #Component and in it I have field ConcurrentHashMap map, which is init in constructor of component and used in spring stream listener:
#Component
public class FooService {
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
fooMap = new ConcurrentHashMap<>();
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body);
}
}
Listener handle messages sent by rest controller. Can you tell me why I always got there fooMap.put(...) NullPointerException because fooMap is null and not initialzied.
EDIT:
After #OlegZhurakousky answer I find out problem is with async method. When I add #Async on some method and add #EnableAsync I can't anymore use private modificator for my #StreamListener method. Do you have idea why and how to fix it?
https://github.com/schwantner92/spring-cloud-stream-issue
Thanks.
Could you try using #PostConstruct instead of constructor?
#PostConstruct
public void init(){
this.fooMap = new ConcurrentHashMap<>();
}
#Denis Stephanov
When I say bare minimum, here is what I mean. So try this as a start, you'll see that the map is not null and start evolving your app from there.
#SpringBootApplication
#EnableBinding(Processor.class)
public class DemoApplication {
private final Map<String, String> map;
public static void main(String[] args) {
SpringApplication.run(DemoRabbit174Application.class, args);
}
public DemoApplication() {
this.map = new HashMap<>();
}
#StreamListener(Processor.INPUT)
public void sink(String string) {
System.out.println(string);
}
}
With Spring everything has to be injected.
You need to declare a #Bean for the ConcurrentHashMap, that will be injected in you Component. So create a Configuration class like:
#Configuration
public class FooMapConfiguration {
#Bean("myFooMap")
public ConcurrentHashMap<Long, String> myFooMap() {
return new ConcurrentHashMap<>();
}
}
Then modify your Component:
#Component
public class FooService {
#Autowired
#Qualifier("myFooMap")
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body); // <= No more NPE here
}
}

#RefreshScope stops #Scheduled task

I have a monitoring app wherein I am running a fixedRate task. This is pulling in a config parameter configured with Consul. I want to pull in updated configuration, so I added #RefreshScope. But as soon as I update the config value on Consul, the fixedRate task stops running.
#Service
#RefreshScope
public class MonitorService {
#Autowired
private AppConfig appConfig;
#PostConstruct
public void postConstRun() {
System.out.println(appConfig.getMonitorConfig());
}
#Scheduled(fixedRate = 1000)
public void scheduledMonitorScan() {
System.out.println("MonitorConfig:" + appConfig.getMonitorConfig());
}
}
AppConfig class just has a single String parameter:
#Configuration
#Getter
#Setter
public class AppConfig {
#Value("${monitor-config:default value}")
private String monitorConfig;
}
As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.
I'm successfully get & override the values from consul config server using RefreshScopeRefreshedEvent
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
#RefreshScope
public class AlertSchedulerCron implements ApplicationListener<RefreshScopeRefreshedEvent> {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
#Value("${pollingtime}")
private String pollingtime;
/*
* #Value("${interval}") private String interval;
*/
#Scheduled(cron = "${pollingtime}")
//#Scheduled(fixedRateString = "${interval}" )
public void task() {
System.out.println(pollingtime);
System.out.println("Scheduler (cron expression) task with duration : " + sdf.format(new Date()));
}
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
// TODO Auto-generated method stub
}
}
Here's how we've solved this issue.
/**
* Listener of Spring's lifecycle to revive Scheduler beans, when spring's
* scope is refreshed.
* <p>
* Spring is able to restart beans, when we change their properties. Such a
* beans marked with RefreshScope annotation. To make it work, spring creates
* <b>lazy</b> proxies and push them instead of real object. The issue with
* scope refresh is that right after refresh in order for such a lazy proxy
* to be actually instantiated again someone has to call for any method of it.
* <p>
* It creates a tricky case with Schedulers, because there is no bean, which
* directly call anything on any Scheduler. Scheduler lifecycle is to start
* few threads upon instantiation and schedule tasks. No other bean needs
* anything from them.
* <p>
* To overcome this, we had to create artificial method on Schedulers and call
* them, when there is a scope refresh event. This actually instantiates.
*/
#RequiredArgsConstructor
public class RefreshScopeListener implements ApplicationListener<RefreshScopeRefreshedEvent> {
private final List<RefreshScheduler> refreshSchedulers;
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
refreshSchedulers.forEach(RefreshScheduler::materializeAfterRefresh);
}
}
So, we've defined an interface, which does nothing in particular, but allows us to call for a refreshed job.
public interface RefreshScheduler {
/**
* Used after refresh context for scheduler bean initialization
*/
default void materializeAfterRefresh() {
}
}
And here is actual job, whose parameter from.properties can be refreshed.
public class AJob implements RefreshScheduler {
#Scheduled(cron = "${from.properties}")
public void aTask() {
// do something useful
}
}
UPDATED:
Of course AJob bean must be marked with #RefreshScope in #Configuration
#Configuration
#EnableScheduling
public class SchedulingConfiguration {
#Bean
#RefreshScope
public AJob aJob() {
return new AJob();
}
}
I have done workaround for this kind of scenario by implementing SchedulingConfigurer interface.
Here I am dynamically updating "scheduler.interval" property from external property file and scheduler is working fine even after actuator refresh as I am not using #RefreshScope anymore.
Hope this might help you in your case also.
public class MySchedulerImpl implements SchedulingConfigurer {
#Autowired
private Environment env;
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(this.taskExecutor());
taskRegistrar.addTriggerTask(() -> {
//put your code here that to be scheduled
}, triggerContext -> {
final Calendar nextExecutionTime = new GregorianCalendar();
final Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();
if (lastActualExecutionTime == null) {
nextExecutionTime.setTime(new Date());
} else {
nextExecutionTime.setTime(lastActualExecutionTime);
nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("scheduler.interval", Integer.class));
}
return nextExecutionTime.getTime();
});
}
}
My solution consists of listening to EnvironmentChangeEvent
#Configuration
public class SchedulingSpringConfig implements ApplicationListener<EnvironmentChangeEvent>, SchedulingConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(SchedulingSpringConfig.class);
private final DemoProperties demoProperties;
public SchedulingSpringConfig(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
LOGGER.info("Configuring scheduled task with cron expression: {}", demoProperties.getCronExpression());
taskRegistrar.addTriggerTask(triggerTask());
taskRegistrar.setTaskScheduler(taskScheduler());
}
#Bean
public TriggerTask triggerTask() {
return new TriggerTask(this::work, cronTrigger());
}
private void work() {
LOGGER.info("Doing work!");
}
#Bean
#RefreshScope
public CronTrigger cronTrigger() {
return new CronTrigger(demoProperties.getCronExpression());
}
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
if (event.getKeys().contains("demo.config.cronExpression")) {
ScheduledTasksRefresher scheduledTasksRefresher = new ScheduledTasksRefresher(triggerTask());
scheduledTasksRefresher.afterPropertiesSet();
}
}
}
Then I use the ContextLifecycleScheduledTaskRegistrar to recreate the task.
public class ScheduledTasksRefresher extends ContextLifecycleScheduledTaskRegistrar {
private final TriggerTask triggerTask;
ScheduledTasksRefresher(TriggerTask triggerTask) {
this.triggerTask = triggerTask;
}
#Override
public void afterPropertiesSet() {
super.destroy();
super.addTriggerTask(triggerTask);
super.afterSingletonsInstantiated();
}
}
Properties definition:
#ConfigurationProperties(prefix = "demo.config", ignoreUnknownFields = false)
public class DemoProperties {
private String cronExpression;
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
}
Main definition:
#SpringBootApplication
#EnableConfigurationProperties(DemoProperties.class)
#EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Based on previous answers I added the following interface and used it on #RefreshScope annotated beans:
public interface RefreshScopeScheduled {
#EventListener(RefreshScopeRefreshedEvent.class)
default void onApplicationEvent() { /*do nothing*/ }
}

Spring : #Scheduled task triggered through the #Controller and Websocket

I have an #Scheduled task which send data to a client every sec throught a websocket.
My need is to start running my scheduled task only when the client ask for it.
Instead of, my task starts when my server starts. it's not the behavior i want.
currently, I have a bean of my scheduled task which is declared in my SchedulingConfigurer :
#Configuration
#EnableScheduling
public class SchedulingConfigurer implements org.springframework.scheduling.annotation.SchedulingConfigurer {
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Bean
public ScheduledTask scheduledTask() {
return new ScheduledTask();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
}
}
Here is my spring controller code :
#MessageMapping("/hello")
public void greeting() throws Exception {
//How do I start my scheduled task here ?
}
Maybe isn't possible to do that with #Scheduled annotation and i have to use the TaskScheduler interface ?
remove #Scheduled declaration from ScheduledTask class
implements Runnable interface instead of
#Component
//#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScheduledTask implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ScheduledTask.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
public void doWork() {
printMessage();
// TODO real work
}
private void printMessage() {
log.info("time to work: {}", dateFormat.format(new Date()));
}
#Override
public void run() {
doWork();
}
}
schedule Your task in controller area like this
#Controller
public class ScheduledTaskController {
#Autowired
private TaskScheduler taskScheduler;
#Autowired
private ScheduledTask scheduledTask;
#RequestMapping(value = "/task/run", method = RequestMethod.GET)
public String runTask() {
// start to run task every 5 sec.
taskScheduler.schedule(scheduledTask, new CronTrigger("0/5 * * * * ?"));
// ok, redirect
return "redirect:/task";
}
}
#Schedule is the declarative way, so not the point you're trying to achieve here.
You could create a Bean using one of the TaskScheduler implementations, such as ThreadPoolTaskScheduler and inject that bean in your application.
It has all the necessary methods to dynamically schedule tasks.

Resources