Jakarta ee 带有许多可选步骤的JEE批处理作业规范

Jakarta ee 带有许多可选步骤的JEE批处理作业规范,jakarta-ee,batch-processing,jsr352,java-batch,jbatch,Jakarta Ee,Batch Processing,Jsr352,Java Batch,Jbatch,有没有办法用JSR352批处理API实现以下逻辑? 我有一系列的步骤,每个步骤都需要根据启动作业时已知的不同条件执行。 条件感知是由外部系统提供的 public List<Steps> createStepSequence(ConditionsEntity conditions) { if (conditions.isStep1Enabled()) { steps.add(step1) } if (conditions.isStep2Enabled()) {

有没有办法用JSR352批处理API实现以下逻辑? 我有一系列的步骤,每个步骤都需要根据启动作业时已知的不同条件执行。 条件感知是由外部系统提供的

public List<Steps> createStepSequence(ConditionsEntity conditions) {
  if (conditions.isStep1Enabled()) {
    steps.add(step1)
  }
  if (conditions.isStep2Enabled()) {
    steps.add(step2)
  }
  if (conditions.isStep3Enabled()) {
    steps.add(step3)
  }
  //many more ifs

return steps;
}
我的测试

public class DecisionPOCTest extends AbstractBatchLOT {

    @Test
    public void testProcess() throws Exception {
        JobOperator jobOperator = getJobOperator();
        Properties properties = new Properties();
        properties.setProperty("isExecuteStep1", "true");
        properties.setProperty("isExecuteStep2", "false");
        properties.setProperty("isExecuteStep3", "true");
        Long executionId = jobOperator.start("poc/decisionPOC", properties);
        JobExecution jobExecution = jobOperator.getJobExecution(executionId);

        jobExecution = BatchTestHelper.keepTestAlive(jobExecution);


        List<StepExecution> stepExecutions = jobOperator.getStepExecutions(executionId);
        List<String> executedSteps = new ArrayList<>();
        for (StepExecution stepExecution : stepExecutions) {
            executedSteps.add(stepExecution.getStepName());
        }

        assertEquals(COMPLETED, jobExecution.getBatchStatus());
        assertEquals(4, stepExecutions.size());
        assertArrayEquals(new String[]{"dummy0", "step1", "dummy2", "step3"}, executedSteps.toArray());
        assertFalse(executedSteps.contains("step2"));
    }
}
公共类DecisionPOCTest扩展了AbstractBatchLOT{
@试验
public void testProcess()引发异常{
JobOperator=getJobOperator();
属性=新属性();
properties.setProperty(“isExecuteStep1”、“true”);
setProperty(“isExecuteStep2”、“false”);
setProperty(“isExecuteStep3”、“true”);
Long executionId=jobOperator.start(“poc/decisionPOC”,属性);
JobExecution JobExecution=jobOperator.getJobExecution(executionId);
jobExecution=BatchTestHelper.KeepTestLive(jobExecution);
List stepExecutions=jobOperator.getStepExecutions(executionId);
List executesteps=new ArrayList();
用于(步骤执行步骤执行:步骤执行){
add(stepExecution.getStepName());
}
assertEquals(已完成,jobExecution.getBatchStatus());
assertEquals(4,stepExecutions.size());
AssertArrayQuals(新字符串[]{“dummy0”、“step1”、“dummy2”、“step3”},executedSteps.toArray());
assertFalse(executedSteps.contains(“step2”);
}
}

看起来失败是因为一个决策在运行时有另一个决策作为下一个执行点。根据JSR 352规范第8.5节,它应该是一个受支持的用例:

作业可以包含任意数量的决策元素。决定因素 是作业级别步骤流中“下一步”属性的目标, 分裂,或其他决定

作为一种解决方法,您可以尝试使用包含相同条件和逻辑的pass-through batchlet步骤。比如说,

<step id="pass-through-step">
   <batchlet ref="PassThroughBatchlet"/>
   <next on="EXECUTE" to="step2"/>
   <next on="SKIP" to="decider2"/>
</step>


或者,如果某些条件逻辑可以通过包含转换元素的batchlet步骤实现,则可以取消这些决策。

看起来失败是由于一个决策在运行时将另一个决策作为其下一个执行点。根据JSR 352规范第8.5节,它应该是一个受支持的用例:

作业可以包含任意数量的决策元素。决定因素 是作业级别步骤流中“下一步”属性的目标, 分裂,或其他决定

作为一种解决方法,您可以尝试使用包含相同条件和逻辑的pass-through batchlet步骤。比如说,

<step id="pass-through-step">
   <batchlet ref="PassThroughBatchlet"/>
   <next on="EXECUTE" to="step2"/>
   <next on="SKIP" to="decider2"/>
</step>


或者,如果您的一些条件逻辑可以通过包含转换元素的batchlet步骤实现,那么您可以取消这些决定。

@cheng有一个很好的答案,这与您正在做的事情相比只是一个很小的改变(您只需要将您的决策器基本上更改为batchlet)

至少对我来说,这是一个有趣的问题,考虑一下这里有什么其他的选择。另一种方法是,在一个决策器中注入所有“IsExecuteStepn”道具,您可以在每个步骤后调用这些道具。该决策器通过一个StepExecution,因此您知道前一步是什么,您可以将其与“isExecute…”道具结合起来,让决策器返回要执行的下一步的id


虽然这可能很聪明,但我认为郑的答案是一个更简单的解决办法。我也认为规范应该考虑允许这样做。不支持这一点的原因可能是为了避免回答这样一个问题:“应该将哪些步骤执行传递给decise方法?”这似乎是可以解决的。

@cheng有一个很好的答案,这与您正在做的事情相比是一个很小的改变(您只需要将decisor更改为Batchlet)

至少对我来说,这是一个有趣的问题,考虑一下这里有什么其他的选择。另一种方法是,在一个决策器中注入所有“IsExecuteStepn”道具,您可以在每个步骤后调用这些道具。该决策器通过一个StepExecution,因此您知道前一步是什么,您可以将其与“isExecute…”道具结合起来,让决策器返回要执行的下一步的id


虽然这可能很聪明,但我认为郑的答案是一个更简单的解决办法。我也认为规范应该考虑允许这样做。不支持这一点的原因可能是为了避免回答这样一个问题:“应该将哪些步骤执行传递给decise方法?”这似乎是可以解决的。

对于这个问题,我有另一种可能的解决方案,它与其他建议的解决方案具有不同的缺点

可以让步骤自行决定是否需要执行任何操作

xml看起来更整洁:

<?xml version="1.0" encoding="UTF-8"?>
<job id="decisionpoc" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <step id="step1" next="step2">
        <batchlet ref="myBatchletWithDecision1">
            <properties>
                <property name="condition" value="isExecuteStep1"/>
            </properties>
        </batchlet>
    </step>
    <step id="step2" next="step3">
        <batchlet ref="myBatchletWithDecision2">
            <properties>
                <property name="condition" value="isExecuteStep2"/>
            </properties>
        </batchlet>
    </step>
    <step id="step3">
        <batchlet ref="myBatchletWithDecision3">
            <properties>
                <property name="condition" value="isExecuteStep3"/>
            </properties>
        </batchlet>
    </step>
</job>
这个测试还没有真正测试预期的行为。实际上我想跳过第2步,但是在当前的解决方案中,第2步确实执行了,但什么也不做。我还没有为此添加测试功能

@Test
public void testProcess() throws Exception {
    JobOperator jobOperator = getJobOperator();
    Properties properties = new Properties();
    properties.setProperty("isExecuteStep1", "true");
    properties.setProperty("isExecuteStep2", "false");
    properties.setProperty("isExecuteStep3", "true");
    Long executionId = jobOperator.start("poc/decisionWithoutDeciderPOC", properties);
    JobExecution jobExecution = jobOperator.getJobExecution(executionId);

    jobExecution = BatchTestHelper.keepTestAlive(jobExecution);


    List<StepExecution> stepExecutions = jobOperator.getStepExecutions(executionId);
    List<String> executedSteps = new ArrayList<>();
    for (StepExecution stepExecution : stepExecutions) {
        executedSteps.add(stepExecution.getStepName());
    }

    assertEquals(COMPLETED, jobExecution.getBatchStatus());
    assertEquals(3, stepExecutions.size());
    assertArrayEquals(new String[]{"step1", "step2", "step3"}, executedSteps.toArray());
}
@测试
public void testProcess()引发异常{
JobOperator=getJobOperator();
属性=新属性();
properties.setProperty(“isExecuteStep1”、“true”);
setProperty(“isExecuteStep2”、“false”);
setProperty(“isExecuteStep3”、“true”);
Long executionId=jobOperator.start(“poc/DecisionWithout DecisionPOC”,属性);
JobExecution JobExecution=jobOperator.getJobExecution(executionId);
jobExecution=BatchTestHelper.KeepTestLive(jobExecution);
List stepExecutions=jobOperator.getStepExecutions(executionId);
List executesteps=new ArrayList();
用于(步骤执行步骤执行:步骤执行){
add(stepExecution.getStepName());
}
assertEquals(已完成,jobExecution.getBatchStatus
<?xml version="1.0" encoding="UTF-8"?>
<job id="decisionpoc" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <step id="step1" next="step2">
        <batchlet ref="myBatchletWithDecision1">
            <properties>
                <property name="condition" value="isExecuteStep1"/>
            </properties>
        </batchlet>
    </step>
    <step id="step2" next="step3">
        <batchlet ref="myBatchletWithDecision2">
            <properties>
                <property name="condition" value="isExecuteStep2"/>
            </properties>
        </batchlet>
    </step>
    <step id="step3">
        <batchlet ref="myBatchletWithDecision3">
            <properties>
                <property name="condition" value="isExecuteStep3"/>
            </properties>
        </batchlet>
    </step>
</job>
@Named
public class MyBatchletWithDecision1 extends AbstractBatchlet {

    @Inject
    @BatchProperty
    private String condition;

    @Inject
    private JobContext jobContext;

    @Override
    public String process() {
        Properties parameters = getParameters();
        String isExecuteStep = parameters.getProperty(condition);
        if (isExecuteStep.equalsIgnoreCase("true")) {
            System.out.println("Running inside a batchlet 1");
        } else {
            //TODO somehow log that the step was skipped
        }
        return "COMPLETED";
    }

    private Properties getParameters() {
        JobOperator operator = getJobOperator();
        return operator.getParameters(jobContext.getExecutionId());

    }
}
@Test
public void testProcess() throws Exception {
    JobOperator jobOperator = getJobOperator();
    Properties properties = new Properties();
    properties.setProperty("isExecuteStep1", "true");
    properties.setProperty("isExecuteStep2", "false");
    properties.setProperty("isExecuteStep3", "true");
    Long executionId = jobOperator.start("poc/decisionWithoutDeciderPOC", properties);
    JobExecution jobExecution = jobOperator.getJobExecution(executionId);

    jobExecution = BatchTestHelper.keepTestAlive(jobExecution);


    List<StepExecution> stepExecutions = jobOperator.getStepExecutions(executionId);
    List<String> executedSteps = new ArrayList<>();
    for (StepExecution stepExecution : stepExecutions) {
        executedSteps.add(stepExecution.getStepName());
    }

    assertEquals(COMPLETED, jobExecution.getBatchStatus());
    assertEquals(3, stepExecutions.size());
    assertArrayEquals(new String[]{"step1", "step2", "step3"}, executedSteps.toArray());
}
<?xml version="1.0" encoding="UTF-8"?>
<job id="decisionpoc" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <!--    This dummy step is needed because it's not possible to start with a decision-->
    <step id="dummy0" next="decider">
        <batchlet ref="dummyBatchlet"/>
    </step>
    <decision id="decider" ref="nextStepDecider">
        <properties>
            <property name="condition" value="isExecuteSteps"/>
        </properties>
<!--        Need to list all steps, see https://stackoverflow.com/questions/59214372/jsr352-decide-next-step-based-on-return-parameter-from-decider-->
        <next on="STEP1" to="step1"/>
        <next on="STEP2" to="step2"/>
        <next on="STEP3" to="step3"/>
        <end on="SKIP"/>
    </decision>
    <step id="step1" next="decider">
        <batchlet ref="myBatchlet1"/>
    </step>
    <step id="step2" next="decider">
        <batchlet ref="myBatchlet2"/>
    </step>
    <step id="step3">
        <batchlet ref="myBatchlet3"/>
    </step>
</job>
@Named
public class NextStepDecider implements Decider {

    @Inject
    @BatchProperty
    private String condition;

    @Inject
    private JobContext jobContext;

    @Override
    public String decide(StepExecution[] ses) throws Exception {
        //FIXME: very hacky code in this method
        if (ses.length != 1) {
            // Decider not reached by transitioning from a step
            return "ERROR";
        }

        Properties parameters = getParameters();
        String executeSteps = parameters.getProperty(condition);
        String[] steps = executeSteps.split(",");

        int start = 0;

        //advance start index to the next step based on the previous step that was executed
        String previousStepName = ses[0].getStepName();
        if (previousStepName.startsWith("step")) {
            start = convertCharToInt(previousStepName);
        }

        //Loop through the remaining steps until we find a step that has its executeStep property set to true
        for (int i = start; i < steps.length; i++) {
            if (steps[i].equalsIgnoreCase("true")) {
                return "STEP" + (i + 1);
            }
        }

        return "SKIP";
    }

    private Properties getParameters() {
        JobOperator operator = getJobOperator();
        return operator.getParameters(jobContext.getExecutionId());
    }

    private int convertCharToInt(String previousStepName) {
        return previousStepName.charAt(previousStepName.length()-1) - '0';
    }
}
@Test
public void testProcess() throws Exception {
    JobOperator jobOperator = getJobOperator();
    Properties properties = new Properties();
    properties.setProperty("isExecuteSteps", "true,false,true");
    Long executionId = jobOperator.start("poc/decisionWithDeciderPOC", properties);
    JobExecution jobExecution = jobOperator.getJobExecution(executionId);

    jobExecution = BatchTestHelper.keepTestAlive(jobExecution);


    List<StepExecution> stepExecutions = jobOperator.getStepExecutions(executionId);
    List<String> executedSteps = new ArrayList<>();
    for (StepExecution stepExecution : stepExecutions) {
        executedSteps.add(stepExecution.getStepName());
    }

    assertEquals(COMPLETED, jobExecution.getBatchStatus());
    assertEquals(3, stepExecutions.size());
    assertArrayEquals(new String[]{"dummy0", "step1", "step3"}, executedSteps.toArray());
    assertFalse(executedSteps.contains("step2"));
}