3

In JSR352 1.0 Final §9.3 Batch Properties, the batch properties is defined to be type java.lang.String:

Batch applications need a way to receive parameters when a job is initiated for execution. Properties can be defined by batch programming model artifacts, then have values passed to them when a job is initiated. Batch properties are string values.

And here's an example:

Properties jobProps = new Properties();
jobProps.setProperty("props1", "value1");
jobProps.setProperty("props2", "value2");

// start
jobOperator.start("myjob", jobProps);

However, I need to use other objects for the job. This general "property" could be any object type, so java.lang.Object. So I'm looking for a work-around solution which meets the below requirements:

  • The property stored should be immutable, which means that once passed to the batch runtime, it won't be changed again.
  • The property should be persisted when job stopped and can be reused when the job restarted. (Serializable?)
  • The work-around solution should not depend on any implementation, e.g. JBeret from JBoss. Because I'm developing a framework, which let the user to choose their own JSR 352 implementation.

Can anybody help?

Mincong Huang
  • 5,284
  • 8
  • 39
  • 62
  • The only way to get the batch container to persist anything for you is to use a chunk reader/writer checkpoint or a step's persistent user data. You could persist something in an initial step but that probably only works if your framework users aren't expected to bring their own complete job (JSL). If you're in an EE environment maybe you should leverage CDI to share objects between the submitting artifact and the batch artifacts themselves. Would be interested to see where you end up... – Scott Kurz Sep 16 '16 at 18:18
  • Thanks @ScottKurz. What does the 2nd sentence _You could persist something ... own complete job (JSL)._ mean? Do you mean the job (JSL) should be defined by the framework itself and should not be overwritten by users? I work for Hibernate Search team as an intern. Our batch job is only defined by Hibernate Search, not by users, so that is OK. The job should work in both Java EE and Java SE, so if CDI can be avoided, it would be better. I'm thinking about using utility class or factory class. – Mincong Huang Sep 16 '16 at 20:06

1 Answers1

3

Here's the idea I had in mind for your use case (even if it does look like a workaround).

You can populate a persistent object for the life of the job instance (i.e. across restarts) by injecting one or more job parameters as batch properties of an initial step, and then using that initial step's persistent user data with the step configured with allow-start-if-complete="true" to ensure the step always runs.


In detail:

The JobOperator interface only lets you pass job parameters to the batch application in the form of a single java.util.Properties object.

So yes, if working around this limitation by using some application or other context (like CDI would provide) isn't a good option, then you'd have to do something like serialize a Serializable (or marshal a JAXB object) and set it as a job parameter.

So taking for granted that you're going to serialize a property named serializedObj, would what the job look like in order to define a job-level object that's persistent and immutable?

Start with a "setup" step like this:

<step id="setup" next="step1" allow-start-if-complete="true">
    <batchlet ref="SetupBatchlet">
        <properties>
            <property name="serializedObj" value="#{jobParameters['serializedObj']}" />
        </properties>  

with an implementation of:

public class SetupBatchlet implements Batchlet, BonusPayoutConstants {

    @Inject
    @BatchProperty(name = "serializedObj")
    private String serializedObjStr;

    @Inject
    private JobContext jobCtx;

    @Inject
    private StepContext stepCtx;

    @Override
    public String process() throws Exception {

        // 1. See if step persistent user data already exists
        MyObject myObj = (MyObject) stepCtx.getPersistentUserData();

        // 2. If not, deserialize
        if (myObj == null) {
            myObj = ...  // deserialize "serializedObjStr"
            // 2a. Persist user data
            stepCtx.setPersistentUserData(myObj);
        }

        // 3. set for use by later steps
        jobCtx.setTransientUserData(myObj);

and now step1 and later steps can do:

MyObject myObj = (MyObject) jobCtx.getTransientUserData();

So this MyObject instance is persistent and immutable. The allow-start-if-complete="true" configuration ensures that the "setup" step always runs, and the JobContext is always populated.

This can scale if you need to aggregate multiple objects though it can get ugly at a certain point; still the same idea can still apply.

Scott Kurz
  • 4,985
  • 1
  • 18
  • 40
  • Your idea is excellent. I didn't know about serialization before asking this question and it helps a lot. My final solution is a little bit different from yours, I use `AbstractJobListener` instead of `Batchlet`, so we don't need another step and the batch property `serializedObj` can still be injected as your suggestion for batchlet. The JSL configuration is very similar too. Other parts work fine. – Mincong Huang Sep 18 '16 at 17:19
  • 1
    Glad you found that helpful. If you're using a job listener though instead of a setup/init step, I wonder which step you're using to store the persistent user data upon? Another option altogether would be to pass the same parameter value on restart (which might require you to persist that value or whatever object produces that value from your submitting environment; if you are able to do this you don't need the step persistent user data. Would be interested to see what you ended up with. – Scott Kurz Sep 19 '16 at 13:39
  • Hi @ScottKurz, I finally have time to implement the entire code. In JBeret (JBoss impl. of JSR352), it seems that the job parameters are persisted automatically when the job (first) starts. So if the job restarts without parameters, the persisted job parameters will be used again. That's why I thought the place of deserialization wasn't important. But I might be wrong. Is the job-restart-role in the same case in WebSphere Batch? – Mincong Huang Sep 30 '16 at 20:05