Skip to content

Even after the second time the job is executed, only the first jobInstanceId can be referenced. #4809

@futokiyo

Description

@futokiyo

Bug description
I am developing an application that executes jobs by executing the TaskExecutorJobLauncher#run(job, jobParameters) method within a RESTful service.
The first time a job is executed, it works as expected.
However, when the job is executed the second time or later,
it is executed with the StepContribution instance and ChunkContext instance from the first execution passed to the tasklet (e.g. the jobInstanceId from the first execution is referenced from the second time or later).
The jobInstanceId value of JobListener and StepListener changes each time a job is executed.

After my investigation, I found that the cause was AbstractMethodInvokingDelegator.java and MethodInvokingTaskletAdapter.java.
https://github.com/spring-projects/spring-batch/blob/v5.2.2/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/MethodInvokingTaskletAdapter.java

https://github.com/spring-projects/spring-batch/blob/v5.2.2/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java

There is a problem with the following code in MethodInvokingTaskletAdapter.java.

	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
		if (getArguments() == null) {
			setArguments(new Object[] { contribution, chunkContext });
		}
		contribution.setExitStatus(mapResult(invokeDelegateMethod()));
		return RepeatStatus.FINISHED;
	}

getArguments() returns null for the first execution of a job, so getArguments() == null become true at the time.

When stepContribution and chunkContext are passed to MethodInvokingTaskletAdapter#execute,

setArguments is called in the first execution of a job, but MethodInvokingTaskletAdapter.arguments is not cleared to null after invokeDelegateMethod() is completed.

From the second job execution onwards, getArguments() no longer returns null, and the a stepContribution instance and a chunkContext instance of the first execution continues to be passed as two arguments to Tacklet#execute implementation.

Are there ways to resolve this issue? Please advise me.

By the way, it also seems that there is a problem with the current execute(StepContribution, ChunkContext) in MethodInvokingTaskletAdapter.java for me.
Shouldn't null be passed to the setArguments method immediately after executing invokeDelegateMethod() as follows to initialize the arguments field of MethodInvokingTaskletAdapter?

	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
		if (getArguments() == null) {
			setArguments(new Object[] { contribution, chunkContext });
		}
		Object obj = null;
		try {
			obj = invokeDelegateMethod();
		} finally {
			setArguments(null);
		}
		contribution.setExitStatus(mapResult(obj));
		return RepeatStatus.FINISHED;
	}

As it stands now, there seems to be an issue where the first job's MethodInvokingTaskletAdapter.arguments continues to reference the instance, preventing it from being released by GC.

Because we need to proceed with development, as a workaround, we have temporarily changed the implementation to one where the StepExecution is temporarily added to a static Cache using the job name and step name as keys in StepListener#beforeStep,
and information equivalent to StepContext is obtained from the StepExecution instance temporarily pushed to Cache,
rather than using the information of chunkContext.getStepContext() passed to the execute method.
(The StepExecution is deleted from the static Cache immediately after the tasklet is executed.)

Environment
Please provide as many details as possible: Spring Batch version, Java version, which database you use if any, etc

Spring Framework version 6.2.5
Spring Batch version 5.2.2

Java version: 17.0.11 (Red_Hat-17.0.11.0+9-1)
Java Distribution: Red Hat build of OpenJDK
JEE AP Server: JBoss EAP 8.0 Update 4.1 (WildFly Core 21.0.11.Final-redhat-00001)
DataBase: Oracle DB express:21.3.0-xe

OS: Windows 11

Steps to reproduce
Steps to reproduce the issue.

Run a job once, then run the same job while changing the timestamp argument.

Expected behavior
A clear and concise description of what you expected to happen.

I would like Tacklet#execute to be passed stepContribution and chunkContext as arguments when each job is executed, just like JobListener and StepListener.

Minimal Complete Reproducible example
Please provide a failing test or a minimal complete verifiable example that reproduces the issue.
Bug reports that are reproducible will take priority in resolution over reports that are not reproducible.

I cannot reveal it at current time as it would violate our confidentiality.

However, if it is absolutely necessary, I will provide a sample app without any confidential information.

I'm sorry, it will take some long time.

Activity

Lavanyaproject1998

Lavanyaproject1998 commented on Apr 16, 2025

@Lavanyaproject1998

public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
if (getArguments() == null) {
setArguments(new Object[] { contribution, chunkContext });
}
Object result = null;
try {
result = invokeDelegateMethod(); // Invoke delegate method
} finally {
setArguments(null); // Clear arguments after execution
}
contribution.setExitStatus(mapResult(result)); // Set exit status based on result
return RepeatStatus.FINISHED; // Mark the step as finished
}

You should modify the execute method to clear the arguments field after each execution. This will ensure that the StepContribution and ChunkContext from a previous execution aren't reused in subsequent executions

futokiyo

futokiyo commented on Apr 16, 2025

@futokiyo
Author

You should modify the execute method to clear the arguments field after each execution.

Tasklet Implementation invoked by MethodInvokingTaskletAdapter is not able to modified.
Because a tasklet implementation is not able to access the arguments field of MethodInvokingTaskletAdapter.

Another candidate method is AbstractMethodInvokingDelegator#invokeDelegateMethod()

https://github.com/spring-projects/spring-batch/blob/v5.2.2/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/adapter/AbstractMethodInvokingDelegator.java

	protected T invokeDelegateMethod() throws Exception {
		MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
		invoker.setArguments(arguments);
		return doInvoke(invoker);
	}

That can be modified as the following code( arguments = null; ).

	protected T invokeDelegateMethod() throws Exception {
		MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
		invoker.setArguments(arguments);
		T result = null;
		try {
			result = doInvoke(invoker);
		} finally {
			arguments = null;
		}
		return result;
	}
fmbenhassine

fmbenhassine commented on May 22, 2025

@fmbenhassine
Contributor

Can you please provide an example that reproduces the issue? I will debug the case to understand the problem. You can find a template with a starting project here: https://github.com/spring-projects/spring-batch/blob/main/ISSUE_REPORTING.md

futokiyo

futokiyo commented on May 23, 2025

@futokiyo
Author

@fmbenhassine -san
Thank you for your reply.

Can you please provide an example that reproduces the issue?

I'm sorry, but I'm currently very busy with my work and my private personal matters and won't be able to respond right away.

I will adjust my tasks to make time. I would appreciate your patience until the end of July.

You can find a template with a starting project here: https://github.com/spring-projects/spring-batch/blob/main/ISSUE_REPORTING.md

I will prepare a sample application for reproduction confirmation and the simple reproduction guide by referring to ISSUE_REPORTING.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @fmbenhassine@futokiyo@Lavanyaproject1998

        Issue actions

          Even after the second time the job is executed, only the first jobInstanceId can be referenced. · Issue #4809 · spring-projects/spring-batch