org.apache.batchee.container.impl.controller.SplitController Maven / Gradle / Ivy
/*
* Copyright 2012 International Business Machines Corp.
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. Licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.batchee.container.impl.controller;
import org.apache.batchee.container.ExecutionElementController;
import org.apache.batchee.container.exception.BatchContainerRuntimeException;
import org.apache.batchee.container.impl.JobContextImpl;
import org.apache.batchee.container.impl.jobinstance.RuntimeFlowInSplitExecution;
import org.apache.batchee.container.impl.jobinstance.RuntimeJobExecution;
import org.apache.batchee.container.services.BatchKernelService;
import org.apache.batchee.container.status.ExecutionStatus;
import org.apache.batchee.container.status.ExtendedBatchStatus;
import org.apache.batchee.container.status.SplitExecutionStatus;
import org.apache.batchee.container.util.BatchFlowInSplitWorkUnit;
import org.apache.batchee.container.util.BatchParallelWorkUnit;
import org.apache.batchee.container.util.FlowInSplitBuilderConfig;
import org.apache.batchee.jaxb.Flow;
import org.apache.batchee.jaxb.JSLJob;
import org.apache.batchee.jaxb.Split;
import javax.batch.operations.JobExecutionAlreadyCompleteException;
import javax.batch.operations.JobExecutionNotMostRecentException;
import javax.batch.operations.JobRestartException;
import javax.batch.operations.JobStartException;
import javax.batch.operations.NoSuchJobExecutionException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
public class SplitController implements ExecutionElementController {
private final static Logger logger = Logger.getLogger(SplitController.class.getName());
private final RuntimeJobExecution jobExecution;
private volatile List parallelBatchWorkUnits;
private final BatchKernelService batchKernel;
private final JobContextImpl jobContext;
private final BlockingQueue completedWorkQueue = new LinkedBlockingQueue();
private final long rootJobExecutionId;
private final List subJobs = new ArrayList();
protected Split split;
public SplitController(final RuntimeJobExecution jobExecution, final Split split, final long rootJobExecutionId,
final BatchKernelService kernelService) {
this.jobExecution = jobExecution;
this.jobContext = jobExecution.getJobContext();
this.rootJobExecutionId = rootJobExecutionId;
this.split = split;
this.batchKernel = kernelService;
}
@Override
public void stop() {
// It's possible we may try to stop a split before any
// sub steps have been started.
synchronized (subJobs) {
if (parallelBatchWorkUnits != null) {
for (BatchParallelWorkUnit subJob : parallelBatchWorkUnits) {
try {
batchKernel.stopJob(subJob.getJobExecutionImpl().getExecutionId());
} catch (final Exception e) {
// TODO - Is this what we want to know.
// Blow up if it happens to force the issue.
throw new IllegalStateException(e);
}
}
}
}
}
@Override
public SplitExecutionStatus execute()
throws JobRestartException, JobStartException, JobExecutionAlreadyCompleteException, JobExecutionNotMostRecentException, NoSuchJobExecutionException {
// Build all sub jobs from partitioned step
buildSubJobBatchWorkUnits();
// kick off the threads
executeWorkUnits();
// Deal with the results.
return waitForCompletionAndAggregateStatus();
}
/**
* Note we restart all flows. There is no concept of "the flow completed". It is only steps
* within the flows that may have already completed and so may not have needed to be rerun.
*/
private void buildSubJobBatchWorkUnits() {
List flows = this.split.getFlows();
parallelBatchWorkUnits = new ArrayList();
// Build all sub jobs from flows in split
synchronized (subJobs) {
for (Flow flow : flows) {
subJobs.add(PartitionedStepBuilder.buildFlowInSplitSubJob(jobExecution.getExecutionId(), jobContext, this.split, flow));
}
for (JSLJob job : subJobs) {
int count = batchKernel.getJobInstanceCount(job.getId());
FlowInSplitBuilderConfig config = new FlowInSplitBuilderConfig(job, completedWorkQueue, rootJobExecutionId);
if (count == 0) {
parallelBatchWorkUnits.add(batchKernel.buildNewFlowInSplitWorkUnit(config, jobExecution.getJobContext()));
} else if (count == 1) {
parallelBatchWorkUnits.add(batchKernel.buildOnRestartFlowInSplitWorkUnit(config, jobExecution.getJobContext()));
} else {
throw new IllegalStateException("There is an inconsistency somewhere in the internal subjob creation");
}
}
}
}
private void executeWorkUnits() {
// Then start or restart all subjobs in parallel
for (BatchParallelWorkUnit work : parallelBatchWorkUnits) {
int count = batchKernel.getJobInstanceCount(work.getJobExecutionImpl().getJobInstance().getJobName());
assert (count <= 1);
if (count == 1) {
batchKernel.startGeneratedJob(work);
} else if (count > 1) {
batchKernel.restartGeneratedJob(work);
} else {
throw new IllegalStateException("There is an inconsistency somewhere in the internal subjob creation");
}
}
}
private SplitExecutionStatus waitForCompletionAndAggregateStatus() {
final SplitExecutionStatus splitStatus = new SplitExecutionStatus();
for (final JSLJob ignored : subJobs) {
final BatchFlowInSplitWorkUnit batchWork;
try {
batchWork = completedWorkQueue.take(); //wait for each thread to finish and then look at it's status
} catch (InterruptedException e) {
throw new BatchContainerRuntimeException(e);
}
final RuntimeFlowInSplitExecution flowExecution = batchWork.getJobExecutionImpl();
final ExecutionStatus flowStatus = flowExecution.getFlowStatus();
aggregateTerminatingStatusFromSingleFlow(null, flowStatus, splitStatus);
}
// If this is still set to 'null' that means all flows completed normally without terminating the job.
splitStatus.setExtendedBatchStatus(ExtendedBatchStatus.NORMAL_COMPLETION);
return splitStatus;
}
//
// A and an uncaught exception are peers. They each take precedence over a , which take precedence over an .
// Among peers the last one seen gets to set the exit stauts.
//
private ExtendedBatchStatus aggregateTerminatingStatusFromSingleFlow(final ExtendedBatchStatus aggregateStatus, final ExecutionStatus flowStatus,
final SplitExecutionStatus splitStatus) {
final String exitStatus = flowStatus.getExitStatus();
final String restartOn = flowStatus.getRestartOn();
final ExtendedBatchStatus flowBatchStatus = flowStatus.getExtendedBatchStatus();
if (flowBatchStatus.equals(ExtendedBatchStatus.JSL_END) || flowBatchStatus.equals(ExtendedBatchStatus.JSL_STOP) ||
flowBatchStatus.equals(ExtendedBatchStatus.JSL_FAIL) || flowBatchStatus.equals(ExtendedBatchStatus.EXCEPTION_THROWN)) {
if (aggregateStatus == null) {
setInJobContext(flowBatchStatus, exitStatus, restartOn);
return flowBatchStatus;
} else {
splitStatus.setCouldMoreThanOneFlowHaveTerminatedJob(true);
if (aggregateStatus.equals(ExtendedBatchStatus.JSL_END)) {
logger.warning("Current flow's batch and exit status will take precedence over and override earlier one from transition element. " +
"Overriding, setting exit status if non-null and preparing to end job.");
setInJobContext(flowBatchStatus, exitStatus, restartOn);
return flowBatchStatus;
} else if (aggregateStatus.equals(ExtendedBatchStatus.JSL_STOP)) {
// Everything but an overrides a
if (!(flowBatchStatus.equals(ExtendedBatchStatus.JSL_END))) {
logger.warning("Current flow's batch and exit status will take precedence over and override earlier one from transition element. " +
"Overriding, setting exit status if non-null and preparing to end job.");
setInJobContext(flowBatchStatus, exitStatus, restartOn);
return flowBatchStatus;
}
} else if (aggregateStatus.equals(ExtendedBatchStatus.JSL_FAIL) || aggregateStatus.equals(ExtendedBatchStatus.EXCEPTION_THROWN)) {
if (flowBatchStatus.equals(ExtendedBatchStatus.JSL_FAIL) || flowBatchStatus.equals(ExtendedBatchStatus.EXCEPTION_THROWN)) {
logger.warning("Current flow's batch and exit status will take precedence over and override earlier one from transition element " +
"or exception thrown. Overriding, setting exit status if non-null and preparing to end job.");
setInJobContext(flowBatchStatus, exitStatus, restartOn);
return flowBatchStatus;
}
}
}
}
return null;
}
private void setInJobContext(ExtendedBatchStatus flowBatchStatus, String exitStatus, String restartOn) {
if (exitStatus != null) {
jobContext.setExitStatus(exitStatus);
}
if (ExtendedBatchStatus.JSL_STOP.equals(flowBatchStatus)) {
if (restartOn != null) {
jobContext.setRestartOn(restartOn);
}
}
}
@Override
public List getLastRunStepExecutions() {
final List stepExecIdList = new ArrayList();
for (final BatchFlowInSplitWorkUnit workUnit : parallelBatchWorkUnits) {
final List stepExecIds = workUnit.getController().getLastRunStepExecutions();
stepExecIdList.addAll(stepExecIds);
}
return stepExecIdList;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy