How to stop a Spring-boot batch job from command line? - spring

I was able to successfully launch a springboot-batch job through command line using CommandLineRunner.
Code :
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
ApplicationContext context;
#Override
public void run(String...args) throws Exception {
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job myJob = context.getBean(args[0], Job.class);
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters();
try {
jobLauncher.run(myJob, jobParameters);
} catch (JobExecutionAlreadyRunningException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Job is successfully started and running using this command
java -jar <jarName> <jobName>
So far so good, But is there any option to stop this batch using command line?

Spring batch by default supports option to stop the job via arg, check out the documentation below:
https://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/core/launch/support/CommandLineJobRunner.html
jobPath <options> jobIdentifier (jobParameters)*
The command line options are as follows
jobPath: the xml application context containing a Job
-stop: (optional) to stop a running execution
jobIdentifier: the name of the job or the id of a job execution (for -stop, -abandon or -restart).
jobParameters: 0 to many parameters that will be used to launch a job specified in the form of key=value pairs.

Both JobExplorer and JobOperator came in handy for me.
Finally got the desired working.
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
ApplicationContext context;
#Autowired
JobExplorer jobExplorer;
#Autowired
JobOperator jobOperator;
#Override
public void run(String...args) throws Exception {
if (args!=null&&args.length<2){
System.out.println("################Jar requires two or more command line args.###########");
return;
}
//Stopping Job
if(args[0].equals("stop")){
Set<JobExecution> jobExecutionsSet= jobExplorer.findRunningJobExecutions(args[1]);
for (JobExecution jobExecution:jobExecutionsSet) {
System.out.println(jobExecution.getStatus()+"ID :"+jobExecution.getId());
if (jobExecution.getStatus()== BatchStatus.STARTED|| jobExecution.getStatus()== BatchStatus.STARTING){
jobOperator.stop(jobExecution.getId());
System.out.println("###########Stopped#########");
}
}
System.out.println("EXITING JOB");
return;
}
else if(args[0].equals("start")){
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job establishmentJob = context.getBean(args[1], Job.class);
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters();
try {
jobLauncher.run(establishmentJob, jobParameters);
} catch (JobExecutionAlreadyRunningException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
To start the job, use the command like this
java -jar <jarName> start <jobName>
And to stop
java -jar <jarName> stop <jobName>

Related

Java CompletableFuture - main class not terminated

I am trying to implment CompletableFuture which invokes a dummy callback method when completed.
However, after adding CompletableFuture.get() method my main class doesn't terminate.
I tried replacing CompletableFuture.get() with Thread.sleep(5000) but it doesn't seem to be right approach.
Please suggest what is causing CompletableFuture.get() to keep blocking even if the thread is complete.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.IntStream;
public class CallableAsyncWithCallBack {
public static void main(String[] args) throws InterruptedException {
CompletableFuture<String> compFuture=new CompletableFuture<String>();
compFuture.supplyAsync(()->{
//Compute total
long count=IntStream.range(Integer.MIN_VALUE, Integer.MAX_VALUE).count();
return ""+count;
}).thenApply(retVal->{
try {
return new CallBackAsynchClass(retVal).toString();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
);
System.out.println("Main Thread 1");
try {
compFuture.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("Lock cleared");
}
}
class CallBackAsynchClass
{
String returnVal="";
public CallBackAsynchClass(String ret) throws InterruptedException, ExecutionException {
System.out.println("Callback invoked:"+ret);
returnVal=ret;
}
#Override
public String toString() {
return "CallBackAsynchClass [returnVal=" + returnVal + "]";
}
}
I am expecting "Lock cleared" to be outputted but .get() seems to be holding up the lock.
.thenApply function returns a new instance of CompletableFuture, and it's this instance that you need to use, try using this way instead :
public class CallableAsyncWithCallBack {
public static void main(String[] args) throws InterruptedException {
CompletableFuture<String> compFuture = CompletableFuture.supplyAsync(() -> {
//Compute total
long count = IntStream.range(Integer.MIN_VALUE, Integer.MAX_VALUE).count();
return "" + count;
});
CompletableFuture<String> future = compFuture.thenApply(retVal -> {
try {
return new CallBackAsynchClass(retVal).toString();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ""; });
System.out.println("Main Thread 1");
try {
future.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("Lock cleared");
}
}
Hope this helps

Using Java Stream API in already multi-threaded environment

My application has its own thread pool(myThreadPool) and I am assigning one of its threads(Producer) to read a file via java stream API. But in runtime stream is lost somewhere and never reaches the print method. But when I run the stream in single threaded environment it works. Does it happen because java stream Api uses its own thread pool underneath or is this conceptually wrong?
public class Processor {
public void process() {
ExecutorService myThreadPool = Executors.newFixedThreadPool(3);
myThreadPool.execute(new Producer());
}
private class Producer implements Runnable{
#Override
public void run() {
try (Stream<String> lines = Files.lines(Paths.get("Path"))) {
System.out.println(lines.count());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I don't know what you have happen. but I can give you an advice (maybe your program exited and Producer is not terminated). copy this code and see what wrong of your code.
public class Processor {
public void process() {
ExecutorService myThreadPool = Executors.newFixedThreadPool(3);
try {
myThreadPool.execute(new Producer());
Thread.currentThread().join();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private class Producer implements Runnable {
#Override
public void run() {
try (Stream<String> lines = Files.lines(Paths.get("Path"))) {
System.out.println(lines.count());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
OR
public class Processor {
public void process() {
ExecutorService myThreadPool = Executors.newFixedThreadPool(3);
try {
myThreadPool.submit(() -> {
new Producer().run();
return null;
}).get();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private class Producer implements Runnable {
#Override
public void run() {
try (Stream<String> lines = Files.lines(Paths.get("Path"))) {
System.out.println(lines.count());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Is there a bug in Spring Batch Step flow function?

In the below piece of code, when StepA fails only StepB and StepC should execute but what actually happens is that all the 3 steps are getting executed! I want to split a spring batch job depending upon whether a step passes or not. I know that there are other ways of doing this by using JobDecider, setting some job parameter, etc but I wanted to know I was doing wrongly here?
#Configuration
#EnableBatchProcessing
public class JobConfig {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
public PlatformTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository jobRepository() {
try {
return new MapJobRepositoryFactoryBean(transactionManager())
.getJobRepository();
} catch (Exception e) {
return null;
}
}
#Bean
public JobLauncher jobLauncher() {
final SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(jobRepository());
return launcher;
}
#Bean
public Job job() {
return jobBuilderFactory.get("job").
flow(stepA()).on("FAILED").to(stepC()).next(stepD()).
from(stepA()).on("*").to(stepB()).next(stepC()).end().build();
}
#Bean
public Step stepA() {
return stepBuilderFactory.get("stepA")
.tasklet(new RandomFailTasket("stepA")).build();
}
#Bean
public Step stepB() {
return stepBuilderFactory.get("stepB")
.tasklet(new PrintTextTasklet("stepB")).build();
}
#Bean
public Step stepC() {
return stepBuilderFactory.get("stepC")
.tasklet(new PrintTextTasklet("stepC")).build();
}
#Bean
public Step stepD() {
return stepBuilderFactory.get("stepD")
.tasklet(new PrintTextTasklet("stepD")).build();
}
#SuppressWarnings("resource")
public static void main(String[] args) {
// create spring application context
final ApplicationContext appContext = new AnnotationConfigApplicationContext(
JobConfig.class);
// get the job config bean (i.e this bean)
final JobConfig jobConfig = appContext.getBean(JobConfig.class);
// get the job launcher
JobLauncher launcher = jobConfig.jobLauncher();
try {
// launch the job
JobExecution execution = launcher.run(jobConfig.job(), new JobParameters());
System.out.println(execution.getJobInstance().toString());
} catch (JobExecutionAlreadyRunningException e) {
e.printStackTrace();
} catch (JobRestartException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JobParametersInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
StepA: is a dummy job which fails i.e it throws some exception
public class RandomFailTasket extends PrintTextTasklet {
public RandomFailTasket(String text) {
super(text);
}
public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
throws Exception {
if (Math.random() < 0.5){
throw new Exception("fail");
}
return RepeatStatus.FINISHED;
}
}
StepB, StepC, StepD are also dummy tasklets:
public class PrintTextTasklet implements Tasklet {
private final String text;
public PrintTextTasklet(String text){
this.text = text;
}
public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
throws Exception {
System.out.println(text);
return RepeatStatus.FINISHED;
}
}
need to have a look at the xml structure that you are using.
Try using Step listener - and then in the after step method you can check the Step status and then you can implement your logic to call the next step or not

Jmeter Java Request

I have a java Request in Jmeter for which I wrote the code extending AbstractJavaSamplerClient and Implemented overridden method
I am hitting and could see the response in the log of remote machine
But I cannot see the response in view Results tree and save responses to file in jmeter
Below is the runtest code snippet,please let me know on how can I capture java request response in Jmeter
public SampleResult runTest(JavaSamplerContext context) {
System.out.println("run Test method actual method is called here..");
XCardService xcardService = null;
String urlString = context.getParameter( "rubyURL" );
SampleResult result = new SampleResult();
boolean success = true;
result.sampleStart();
//try with Junit
String[] rubyURL = new String[1];
rubyURL[0] = urlString;
try {
System.out.println("RubyUrl::"+rubyURL);
xcardService = XCardFactory.getService(rubyURL, 165, appPassword, 5000);
} catch (AuthenticationFailureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IncompatibleVersionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServiceUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServiceInitFailedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XCardTimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DiagnosticContext dc = new ServerDiagnosticContext("Junit TestCase");
try {
System.out.println("xcardService::"+xcardService);
AccountInfo account = xcardService.getAccountInfo(dc, 1089765);
System.out.println("getAccount Info ::"+account.toString());
} catch (InvalidArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClientNotAuthenticatedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XCardTimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XCardException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
result.sampleEnd();
result.setSuccessful(success);
System.out.println("Response Message:::"+result.getResponseMessage());
return result;
}
A good example of an implementation of AbstractJavaSamplerClient is org.apache.jmeter.protocol.java.test.SleepTest.
http://www.javadocexamples.com/java_source/org/apache/jmeter/protocol/java/test/SleepTest.java.html
As a general rule i would put the result.sampleEnd(); in a finally block.

Locale.getDefault(Locale.Category.FORMAT) using reflection in java

*import java.util.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.*;
public class LocaleProgram {
public static void main(String[] args) {
try {
Class c = Class.forName("java.util.Locale");
Class c1 = Class.forName("java.util.Locale$Category");
Class[] paramTypes = { c1 };
try {
Method m = c.getMethod("getDefault", paramTypes);
try {
//Object o = m.invoke(c1);
Object o = m.invoke(c1,new Object[]{"FORMAT"});
System.out.println("Object:" + (Locale)o);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}*
Basically I want Locale.getDefault(Locale.Category.FORMAT) using reflection, so that I can run this code on other than Java version 7.But I am trouble with calling invoke method, it's giving me java.lang.IllegalArgumentException: argument type mismatch exception.
Many thanks in advance
Change this line
Object o = m.invoke(c1,new Object[]{"FORMAT"});
to
Object o = m.invoke(c1,new Object[]{Locale.Category.FORMAT});
However, this may not be what you want, as you want to create the used enum by using reflection?
This will not work under Java 1.6 as these enums didn't exist yet.
Some handy stuff to read: http://blog.ej-technologies.com/2011/12/default-locale-changes-in-java-7.html
As Timmos stated the IllegalArgumentException results from the fact that you pass a String instead of a Locale.Category enum constant. The correct way to get the Locale.Category.FORMAT is by using java.lang.reflect.Field class:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
public class GetDefaultLocale {
public static void main(String[] args){
Locale locale = null;
try {
Class<?> LOCALE_CATEGORY_Class = Class.forName("java.util.Locale$Category");
Class<?> LOCALE_Class = Class.forName("java.util.Locale");
Class<?>[] paramTypes = { LOCALE_CATEGORY_Class };
Method m = LOCALE_Class.getMethod("getDefault", paramTypes);
Field FORMAT_Field = LOCALE_CATEGORY_Class.getField("FORMAT");
//we pass null because the FORMAT is an enumeration constant(the same applies for class variables)
Object FORMAT = FORMAT_Field.get(null);
locale = (Locale)m.invoke(LOCALE_CATEGORY_Class, new Object[]{FORMAT});
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//for jre 6
if(locale == null){
locale = Locale.getDefault();
}
}

Resources