Adding job parameter in before job doesnt work spring batch - spring-batch

I have a spring batch job wherein I need to set job parameter in beforeStep
I am using below code for the same:
#Override
public void beforeJob(JobExecution jobExecution) {
String pid = fetchPid();
jobExecution
.getJobParameters()
.getParameters()
.put("pid", new JobParameter(pid));
}
When I run above code and debug , I see that pid is not present in the jobparameters . What could be wrong here?

What could be wrong here?
The JobParameters#getParameters returns a unmodifiable map of parameters (See Javadoc). So adding the pid key as you did it wont work.
I need to set job parameter in beforeStep
I guess you mean in beforeJob and not beforeStep since your code shows the beforeJob method. Using the JobExecutionListener to add parameters is too late because the parameters are used to identify the instance, and at the time of invoking beforeJob, the execution has been already launched with the given parameters. You need to prepare the parameters upfront then use them to launch the job using jobLauncher.run(job, jobParameters).

Related

How to perform logic after ItemWriter has completed?

Hi all I'm new to SO and to Springbatch. I have written a batch job with Classifer that updates a table in two different ways (i.e. two ItemWriters) depending on what's retrieved through the ItemReader and all that's working fine. Now, I want to perform some logic after the ItemWriters are done updating. I want to do some logging and update another table with the same set of data retrieved previously. How can I achieve this? I looked at ItemWriterListener but seems it cannot perform data specific logics. I did some searching but with no luck. Any help would be appreciated. Thanks in advance!!
You can try using StepExecutionListener implementing it to your Writer Class to execute logic once ItemWriter is done with the execution. Below is a snippet of the ItemWriter for your reference,
public class TestWriter implements ItemWriter<Test>, StepExecutionListener {
#Override
public void beforeStep(StepExecution stepExecution) {
}
#Override
public void write(List<? extends Test> items) throws Exception {
// Logic of Writer
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
// You can perform post logic after writer here inside afterStep based on your requirements
// Return custom exit status based on the run
return ExitStatus.COMPLETED;
}
}
Now, I want to perform some logic after the ItemWriters are done updating. I want to do some logging and update another table with the same set of data retrieved previously. How can I achieve this? I looked at ItemWriterListener but seems it cannot perform data specific logics.
Since you want to do something with the same items retrieved previously, you need to use a ItemWriteListener#afterWrite as this method gives you access to the items that have just been written.
EDIT: Add details about the failure case based on comments
If the transaction is rolled back, the method ItemWriteListener#onWriteError will be called. Please find more details about this in the common patterns section.

Spring Batch: How do I use decider based on my Reader result?

I am new to Spring Batch. I found out that by use of ExecutionContextPromotionListener, I can set up key value pairs and get them in future steps.
<step id="step1">....</step>
<decision id="decision1">.... />
When I used Tasklet instead of reader, I did following:
Created bean of ExecutionContextPromotionListener with expected keys in my batch config file.
Registered the Listener to my step.
Put the key-value pairs in executionContext retrieved from chunkContext inside my Tasklet like below:
Now the Decider can read from the step execution context as follows and decide.
But, I want to take decision based on Reader from previous step. So, in my decision, how to get the value from Reader? Or is my approach wrong. Please suggest.
simple way is you can make use of Execution context from your step and can pass on the values into the next step .
So, in you first step do something like this
// ...
ExecutionContext stepContext = this.stepExecution.getExecutionContext();
stepContext.put("DATA_KEY", dataToShare );
then in your next step you can read this using the execution context.
ExecutionContext jobContext = jobExecution.getExecutionContext();
dataToShare = jobContext.get("DATA_KEY");
you just need to manage the keys - the key that you use to put in first step and read it in next step

Jbehave : GivenStories in the end of execution

I'm using a GivenStories for executing Login scenario which is located in different story.
I was wondering if there is a way to use something similar in order to execute a logout story which is also located in different story than one I actually executing.
I know that I can do some tricks with #before/after annotations , but the question is if I can execute a "post" story
Thanks
Based on the jBehave annotation documentation a post story step can be implemented by annotating a step class method with #AfterStory (or #AfterStories if you want to execute only after all stories complete). The #AfterStory method will execute regardless of whether your executing story contains a step from the related step class (i.e. is guaranteed to execute after every story - see below for restricting to given stories).
The #BeforeStory and #AfterStory annotations allow the corresponding
methods to be executed before and after each story, either a
GivenStory or not:
#AfterStory // equivalent to #AfterStory(uponGivenStory=false)
public void afterStory() {
// ...
}
#AfterStory(uponGivenStory=true)
public void afterGivenStory() {
// ...
}
This is the answer I got from the jbehave dev channel.
Hi,
there is no such mechanism, but you could:
use the Lifecycle to execute steps (not stories) after the execution
of a scenario (executed after each scenario) have a final scenario
which invokes the given stories

Exchanging data between steps in spring batch

I have a job that is built of the following components
Processing Step - activates an external processing that logs it's result in the DB and returns an internal id so I can take this id and process it further
Logging Step - built of a tasklet, that contains a chunk
The chunk is built of an item reader - that I plan will use #{stepExecutionContext['job.id']} as part of the sql written in the xml file so it will get the relevant logging info
I'm trying to work with the solution suggested here - 11.8 Passing Data to Future Steps but i get this error when i try to add a property on the step execution context or the job execution context
chunkContext.getStepContext().getStepExecutionContext().put("job.id", jobId);
And I get this error:
java.lang.UnsupportedOperationException: null
at java.util.Collections$UnmodifiableMap.put(Collections.java:1342)
at ...
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:395)
Did i miss something?
StepContext available from ChunkContext is a read-only object; use a StepExecutionListener and save the step execution context passed as parameter in method StepExecutionListener.afterStep(StepExecution stepExecution)
I always follow this path:
chunkContext
.getStepContext()
.getStepExecution()
.getJobExecution()
.getExecutionContext()
.put("", "");
I also had problems doing the put in StepExecution.
Maybe it loses data when it goes to the next step.
However, I leave the last sentence to the most expert.
chunkContext.getStepContext().getStepExecutionContext() retrun copy use chunkContext.getStepContext().getStepExecution().getExecutionContext()

How to make EF log sql queries globally?

How do I "tell" EF to log queries globally? I was reading this blog post: EF logging which tells in general how to log sql queries. But I still have a few questions regarding this logger.
Where would I need to place this line context.Database.Log = s =>
logger.Log("EFApp", s);?
Can it be globally set? Or do I have to place it everywhere I do DB
operations?
In the "Failed execution" section, the blogger wrote that, and I
quote:
For commands that fail by throwing an exception, the output contains the message from the exception.
Will this be logged too if I don't use the context.Database.Log?
Whenever you want the context to start logging.
It appears to be done on the context object so it should be done every time you create a new context. You could add this line of code in your constructor though to ensure that it is always enabled.
It will not log if you do not enable the logging.
I don't recommend to use that's functionality, because, it hasn't reason to exists in the real case.
Thats it use a lot of to debug code only. But, wether you wanna know more than details ... access link... https://cmatskas.com/logging-and-tracing-with-entity-framework-6/
In this case you can put code like this
public void Mylog()
{
//Thats a delegate where you can set this property to log using
//delegate type Action, see the code below
context.Database.Log = k=>Console.Write("Any query SQL")
//Or
context.Database.Log = k=>Test("Any query SQL")
}
public void Test(string x){
Console.Write(x)
}
I hope thats useufull