How to correctly stop the TimerTask - java

So basically I have a task with a delay that kills a VPN pod. I don't want to have a running pod when it's not needed.
The Desired behavior is when the service receives a request(REST) it cancels the existing task and creates a new one with further delay.
In my solution I use thread.stop() to cancel my task which has been deprecated for a while.
...
var VPN_TIMER_THREAD_NAME = "vpn-timer";
for (var thread : Thread.getAllStackTraces().keySet()) {
if (thread.getName().equals(VPN_TIMER_THREAD_NAME)) {
// Interrupted doesn't work for me
thread.stop();
}
}
var timer = new Timer(VPN_TIMER_THREAD_NAME);
timer.schedule(new TimerTask() {
#Override
public void run() {
// New Transaction for EM
TransactionStatus tx = VpnServiceImpl.this.txManager.getTransaction(new DefaultTransactionDefinition());
try {
var vpnToUpdate = VpnServiceImpl.this.em.find(Vpn.class, vpn.getId());
doTearDown(vpnToUpdate);
VpnServiceImpl.this.txManager.commit(tx);
} catch (RuntimeException e) {
log.error("Tear Down Error {}.", e.getMessage());
VpnServiceImpl.this.txManager.rollback(tx);
}
}
}, this.vpnProperties.delay());
...
private VpnStatusS2SDto doTearDown(Vpn vpn) {
log.debug("In the tear down");
this.client
.pods()
.inNamespace(this.kubeProps.getNamespace())
.withLabel("app", "vpn-gateway")
.withLabel("app.kubernetes.io/component", "vpn")
.delete();
entity.setModifiedDate(Instant.now());
this.em.persist(entity);
return mapper.toVpnStatusDto(entity);
}
When I'm changing to thread.interrupt() the doTearDown method is invoking more than once if I make more than one request.
With thread.stop it "kills" the previous task and creates a new one, indeed the tear down has been invoked only once.
I'm using Spring Boot.
Is there any way to implement that behavior?
Thanks in advance

According to the answer from ewramner I found the solution. It works as expected. Every new request cancels the existing task and creates a new one.
I've created the nested task class:
private class ShutDownTask extends TimerTask {
private final Vpn vpn;
private final PlatformTransactionManager txManager;
private final EntityManager em;
ShutDownTask(Vpn vpn, PlatformTransactionManager txManager, EntityManager em) {
this.vpn = vpn;
this.txManager = txManager;
this.em = em;
}
#Override
public void run() {
TransactionStatus tx = this.txManager.getTransaction(new
DefaultTransactionDefinition());
try {
var vpnToUpdate = this.em.find(Vpn.class, this.vpn.getId());
doTearDown(vpnToUpdate);
this.txManager.commit(tx);
} catch (RuntimeException e) {
this.txManager.rollback(tx);
}
}
}
In my service class:
#Service
public class VpnServiceImpl {
...
private final PlatformTransactionManager txManager;
private final EntityManager em;
private ShutDownTask shutDownTask;
...
if (this.shutDownTask != null) {
this.shutDownTask.cancel();
}
var timer = new Timer("vpn-timer");
this.shutDownTask = new ShutDownTask(vpn, this.txManager, this.em);
timer.schedule(this.shutDownTask, this.vpnProperties.delay());
...
}

Related

Get the spring boot scheduled cron expression from outside jar file

I have a spring boot java service I have to schedule to run on a particular time. I have enabled the #Enablescheduling and #Scheduled annotation and given the cron expression.
It's working fine. The scheduler is running at the expected time. But my concern is I should control the cron expression somewhere from outside my jar file. I have tried using it in property file but when packaging my property file also getting included in that.
Sample code:
#PostMapping(path = "getoktatodynamodb")
#Scheduled(cron = "0 0/5 0 * * ?")
#ApiOperation("Sync data to DynamoDB")
public FinalResponse getdatatodynamodb() {
FinalResponse finalResponse = new FinalResponse();
try {
LOGGER.info("Sync data to DynamoDB starts - " + new Date());
finalResponse = dynamodbuserService.dynamoDbSync();
} catch (MyRestTemplateException ex) {
LOGGER.error(ex.getMessage());
finalResponse.setResponseMessage(ex.getMessage());
finalResponse.setStatusCode(ex.getStatusCode().value());
} catch (Exception execption) {
LOGGER.error(execption.getMessage());
finalResponse.setResponseMessage(execption.getMessage());
finalResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
} finally {
LOGGER.info("Sync data DynamoDB Ends - " + new Date());
}
return finalResponse;
}
The main intention is scheduler should be in our control whenever we need to change the time it should be configurable. No code change and restarting the scheduler for minor changes.
How should we achieve this also we would like to schedule this in linux ec2 instance? in case if we have better suggestion to achieve this kindly share it.
You can implement SchedulingConfigurer:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/SchedulingConfigurer.html
This DZone article shows a really good example: https://dzone.com/articles/schedulers-in-java-and-spring which I'm showing here in case the article doesn't stay permanent.
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
TaskScheduler taskScheduler;
private ScheduledFuture<?> job1;
private ScheduledFuture<?> job2;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler =new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threads
threadPoolTaskScheduler.setThreadNamePrefix("scheduler-thread");
threadPoolTaskScheduler.initialize();
job1(threadPoolTaskScheduler);// Assign the job1 to the scheduler
// Assign the job1 to the scheduler
this.taskScheduler=threadPoolTaskScheduler;// this will be used in later part of the article during refreshing the cron expression dynamically
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
private void job1(TaskScheduler scheduler) {
job1 = scheduler.schedule(new Runnable() {
#Override
public void run() {
System.out.println(Thread.currentThread().getName() + " The Task1 executed at " + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, new Trigger() {
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp = "0/5 * * * * ?";// Can be pulled from a db .
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
private void job2(TaskScheduler scheduler){
job2=scheduler.schedule(new Runnable(){
#Override
public void run() {
System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date());
}
}, new Trigger(){
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String cronExp="0/1 * * * * ?";//Can be pulled from a db . This will run every minute
return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
}
});
}
}

Cannot receive another websocket message after interruption of thread with websocket session

I have problem with receiving new messages from my JS client after I use the first received message in new thread and cancel the thread. I use spring boot for the back end. The interesting thing is that the session is not closed but I just cannot receive any more messages after the first one when I interrupt my thread.
Here is my websocket config:
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketHandler(), "/socket1").setAllowedOrigins("*");
}
}
Here is my handler with the executor service:
public class WebSocketHandler extends AbstractWebSocketHandler {
Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
Test test = new Test();
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.info("START");
List<UrlWithPageNumber> listings = new ArrayList<>();
listings.add(new UrlWithPageNumber( "www.somesite.com", 1));
listings.add(new UrlWithPageNumber( "www.anothersite.com", 1));
listings.add(new UrlWithPageNumber( "www.thirdsite.com", 1));
checkItemsAsync(listings, session);
logger.info("DONE");
session.sendMessage(new TextMessage("DONE"));
}
public void checkItemsAsync(List<UrlWithPageNumber> listings, WebSocketSession session) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
final CountDownLatch latch = new CountDownLatch(listings.size());
for (UrlWithPageNumber listing : listings) {
executorService.submit(() -> {
if(Test.stop) {
return;
}
ListingInfo listingInfo = test.itemPage(listing.getLink(), 1, 1);
logger.info(listingInfo.toString());
synchronized(session) {
try {
session.sendMessage(new TextMessage(listingInfo.toString()));
} catch (IOException e) {
e.printStackTrace();
}
}
latch.countDown();
});
}
try {
latch.await();
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
}
Now the problem is the following: I call my handleTextMessage method from JS client and the execution is starting then I press another button in my page and it is changing Test.stop boolean flag to true and in that way i stop the remaining threads to be executed. After that if I call handleTextMessage from the js like the first time it is not called. I checked if I close the WebSocketSession and then try to call the backend and the result is similar but in my case the session is not closed for sure ! The question is how to use the WebSocketSession many times and why the session is become broken when I use it in my executor service and stop the thread? Should I do something with the session if I shutdown threads or this is just some spring boot bug ?
The problem is in the latch it wait more than expected because I skip some threads..

Kubernetes Watch - How to update? (Java API)

I have two tasks that I run in parallel threads and I'm pulling my hair on why the Watch functionality doesn't work. Please let me know if you have any insights.
Task 1: Get the status of the pods and show the current status.
Task 2: Keep a watch on new events. This is what I'm trying to understand better.
Each of these tasks are executed every 30 seconds with scheduledAtFixedRate().
Expected behavior:
Task 1: I should get the list of all the pods with their current status (this works).
Task 2: I should expect list of new events as they happen.
Observed behavior:
Task 1: It works fine. I get updated status of the pods every 30 seconds.
Task 2: It dumps the events from the first request, but it seems like it doesn't update any new events.
Code:
Task 1:
#Component
#Scope(value = org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Task1 implements Runnable {
private ScheduledExecutorService scheduledExecutorService;
private CommandInvoker commandInvoker;
private static final int INITIAL_DELAY = 15;
private static final int POLLING_INTERVAL = 30;
#Autowired
public Task1 (CommandInvoker commandInvoker,
ScheduledExecutorService scheduledExecutorService) {
this.commandInvoker = commandInvoker;
this.scheduledExecutorService = scheduledExecutorService;
this.scheduledExecutorService.scheduleAtFixedRate(this, INITIAL_DELAY, POLLING_INTERVAL, TimeUnit.SECONDS);
}
#Override
public void run() {
System.out.println("===== STARTING TASK 1 POD HEALTH CHECK =======");
commandInvoker.getPodStatus();
}
}
Task 2:
#Component
#Scope(value = org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Task2 implements Runnable {
private ScheduledExecutorService scheduledExecutorService;
private CommandInvoker commandInvoker;
#Autowired
public Task2(CommandInvoker commandInvoker,
ScheduledExecutorService scheduledExecutorService) {
this.commandInvoker = commandInvoker;
this.scheduledExecutorService = scheduledExecutorService;
this.scheduledExecutorService.scheduleAtFixedRate(this, 30, 30, TimeUnit.SECONDS);
}
#Override
public void run() {
System.out.println("===== STARTING TASK 2 EVENT WATCH UPDATE =======");
commandInvoker.getWatchUpdates();
}
}
CommandInvoker:
#Component
public class CommandInvoker {
public void getPodStatus() {
try {
CoreV1Api api = new CoreV1Api();
V1PodList list = api.listPodForAllNamespaces(null,
null, null, null, null, null, null, null, null);
for( V1Pod pod : list.getItems() ) {
// THIS WORKS //
}
} catch ( ApiException e) {
throw new WhateverException ("Failed to handle watchlist event", e);
}
}
public void getWatchUpdates() {
CoreV1Api api = new CoreV1Api();
try {
Watch<V1Event> watch = Watch.createWatch(
apiClient,
api.listEventForAllNamespacesCall(null, null, null, null,
null, null, null, null, true, null, null),
new TypeToken<Watch.Response<V1Event>>() {}.getType());
watch.forEach( response -> {
V1Event event = response.object;
// THIS ONLY DUMPS EVENTS FROM FIRST CALL BUT NEVER GETS EXECUTED AGAIN
});
// I NEVER REACH HERE BUT I DON'T GET ANY UPDATES
} catch ( ApiException e) {
throw new K8ServerException("Failed to handle watchlist event", e);
}
}
}

Why is my Spring #Async bean method not being executed asychronously?

I have a Springboot application and I'm trying to execute an asynchronous method on a bean class inside a controller method. The problem is that my #Async method is not being executed asynchronously. Execution is halted until the method completes.
Can anyone tell me what I'm missing?
Here is my application class:
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
connector.setPort(9000);
connector.setAsyncTimeout(60000);
}
});
return factory;
}
}
Here is my bean class:
public class LongProcess {
#Async
public Future<String> call() {
try {
System.out.println("Sleeping now...");
Thread.sleep(10000);
return new AsyncResult<String>("Hey");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
My configuration class:
#Configuration
#EnableAsync
public class LongProcessConfiguration implements AsyncConfigurer {
#Bean
public LongProcess longProcessBean() {
return new LongProcess();
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(10);
taskExecutor.setThreadNamePrefix("LULExecutor-");
taskExecutor.initialize();
return taskExecutor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
My controller method:
#RequestMapping("/utilities/longProcess")
public String longProcess() {
System.out.println("Starting long process...");
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
Future<String> result = process.call();
System.out.println("Done!");
return "{success: 1}";
}
This request unfortunately does not return immediately (I don't care about the result). The method is called successfully, but not in the background. Any idea what I might be missing?
As a test, if I change the controller method to wait for the result, the wait block is never entered:
#RequestMapping("/utilities/longProcess")
public String longProcess() throws InterruptedException {
System.out.println("Starting long process...");
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
LongProcess process = context.longProcessBean();
Future<String> result = process.call();
while (!(result.isDone())) {
Thread.sleep(1); //10-millisecond pause between each check
System.out.println("Waiting for Long Process...");
}
System.out.println("Done!");
return "{success: 1}";
}
You have a mistake for the CDI usage.
If you manage your object using Spring Container you have to get deal just with ApplicationContext or its abilities like #Autowired.
The code
CsvFileDifferConfiguration context = new CsvFileDifferConfiguration();
is wrong.
Since you define your LongProcess as a #Bean you can just inject it to your #Controller:
#Autowired
privete LongProcess process;
and use it as before.
Using objects directly (e.g. new) loses the dependency injection features.
Read more Spring Docs, please.

TimerService EJB does not get canceled if used with EJB Interceptor

Hi every one i am having an issue with timer service, on timeout it perform some business logic if there is any exception in business logic. which is catched in one of interceptor as well as in timer service. then timer service is not canceled. timer.cancel();
So how it works my timer service get exectued on timeout, it calls an ejb function, this ejb has an interceptor associated with it. if ejb function has any exception. Interceptor is basically logging that exception. then i have a code which stops the timer, it should work in case of success or failure, but it only stop timer in case of success. in case of failure(when exception occurs) it does not cancel the timer. so every time i restart jboss it try to excute the timer again (as it was failed).
My Question is how can i stop timer in case of success and failure(exception). Please note i am using Interceptor as well (Which i can not remove, while removing it fixes the issue).
Here is my different code classes which may help in understanding the issue.
Business Logic Bean
#Stateless
#Local(UtilityLocal.class)
#Interceptors({ ExceptionInterceptor.class })
public class UtilityEJB implements UtilityLocal {
public void doDomeThing(MazTimerTask task) {
System.out.println("--- Business logic ---");
Integer x = 5/0; // this will generate an exception
System.out.println("--- Business logic ---");
}
}
Interceptor
public class ExceptionInterceptor {
#Resource
private javax.ejb.SessionContext ctx;
#AroundInvoke
public Object aroundInvoke(final InvocationContext invocationContext)
throws Exception { //NOPMD
try {
return invocationContext.proceed();
}
catch (Exception exception) {
System.out.println(" Logg the exception here");
throw exception;
}
}
}
Timer Service
#Stateless
public class MazTimer implements MazTimerLocal, MazTimerRemote{
#EJB
private transient UtilityLocal ejb;
#Resource
private transient TimerService timerService;
#Timeout
#TransactionTimeout(3600)
public void handleTimeout(Timer timer) {
MazTimerTask task = (MazTimerTask) timer.getInfo();
if (task != null ) {
try{
ejb.doDomeThing(task);
}catch(Exception e) {
System.out.println("------------ Exception occured : " + task.getName());
}
finally {
stopTimer(task);
}
}
}
public void startTimer(MazTimerTask task) {
timerService.createTimer(new Date(), 10, task);
}
private void stopTimer(MazTimerTask task) {
try {
Timer timer = getTimer(task);
if (timer != null) {
timer.cancel();
System.out.println("------------ Timer stopped : " + task.getName());
}
} catch (RuntimeException e) {
}
}
private Timer getTimer(Serializable timerId) {
Timer timer = null;
if (timerId != null) {
Iterator<Timer> it = timerService.getTimers().iterator();
while (it.hasNext()) {
Timer currentTimer = it.next();
if (currentTimer.getInfo().equals(timerId)) {
timer = currentTimer;
break;
}
}
}
return timer;
}
}

Categories

Resources