Accesing JobContext from a partitioned step in JSR

2020-06-21 04:41发布

问题:

I'm trying to pass an object between batchlets, but I've encountered a problem when trying to access the jobContext from a partitioned step (batchlet).

According to the JSR 352 Specification

9.4.1.1 Batch Context Lifecycle and Scope: A batch context has thread affinity and is visible only to the batch artifacts executing on that particular thread. A batch context injected field may be null when out of scope. Each context type has a distinct scope and lifecycle as follows: 1. JobContext There is one JobContext per job execution. It exists for the life of a job. There is a distinct JobContext for each sub-thread of a parallel execution (e.g. partitioned step). 2. StepContext There is one StepContext per step execution. It exists for the life of the step. For a partitioned step, there is one StepContext for the parent step/thread; there is a distinct StepContext for each sub-thread.

My (failed) solution was to use the JobContext.setTransientUserData, but because the partitioned step uses a distinct JobContext I can't get the TransientUserData.

Is there an alternative to what I'm trying to do? Using PartitionMapper properties it's not possible because I need to pass an object, not a string to every partition.

To be clear, I need to do this:

  1. NormalBatchlet -> Save an object to be used in the next step.
  2. PartitionedBatchlet -> Obtain the saved object in previous step. This object isn't a simple String so using PartitionMapper properties is not a solution.

UPDATE

I'm now using a simple Singleton EJB with a HashMap to store objects between steps and when the job is finished I clear this map to avoid resources leak.

This is a workaround because I really want to use only the javax.batch package and not depend on EJB's, so I'm not putting it as an answer.

回答1:

You could try something like this which should conform with the current spec programming model.

Store the object from your first step using the persistent step data in your NormalBatchlet:

stepCtx.setPersistentUserData(mySerializableData);

Retrieve the data from the first step in your partitions, by looking up the previous step:

Long execId = jobCtx.getExecutionId();

List<StepExecution> stepExecs = jobOperator.getStepExecutions(execID);

MyPersistentUserData myData;

for (StepExecution step : stepExecs) {
    if (step.getStepName().equals("somePreviousStepX") {
        myData = (MyPersistentUserData)step.getPersistentUserData();
    }
}

//use myData


回答2:

There is not a spec-defined way to share the transient user data between the JobContext of the main thread and the partition-level JobContext(s). This is an understandable point of confusion, but this is indeed the intended design.



回答3:

In specification mentioned an only one way to access to Context - Injection. See paragraph 9.4.1.

"9.4.1 Batch Contexts

Batch artifact access to batch contexts is by injection using the standard @Inject annotation (javax.inject.Inject). A field into which a batch context is injected must not be static and must not be final.

E.g.:

@Inject JobContext _jctxt;

@Inject StepContext _sctxt;

The batch runtime is responsible to ensure the correct context object is injected according to the job or step currently executing."