/*
 * Copyright 2013 the original author or authors.
 *
 * 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.springframework.batch.core.jsr.step;

import java.util.Collection;

import javax.batch.api.partition.PartitionReducer;
import javax.batch.api.partition.PartitionReducer.PartitionStatus;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.jsr.partition.support.JsrStepExecutionAggregator;
import org.springframework.batch.core.partition.PartitionHandler;
import org.springframework.batch.core.partition.StepExecutionSplitter;
import org.springframework.batch.core.partition.support.StepExecutionAggregator;
import org.springframework.batch.item.ExecutionContext;

/**
 * An extension of the {@link PartitionStep} that provides additional semantics
 * required by JSR-352.  Specifically, this implementation adds the required
 * lifecycle calls to the {@link PartitionReducer} if it is used.
 *
 * @author Michael Minella
 * @since 3.0
 */
public class PartitionStep extends org.springframework.batch.core.partition.support.PartitionStep {

	private PartitionReducer reducer;
	private boolean hasReducer = false;
	private StepExecutionAggregator stepExecutionAggregator = new JsrStepExecutionAggregator();

	public void setPartitionReducer(PartitionReducer reducer) {
		this.reducer = reducer;
		hasReducer = reducer != null;
	}

	/**
	 * Delegate execution to the {@link PartitionHandler} provided. The
	 * {@link StepExecution} passed in here becomes the parent or master
	 * execution for the partition, summarizing the status on exit of the
	 * logical grouping of work carried out by the {@link PartitionHandler}. The
	 * individual step executions and their input parameters (through
	 * {@link ExecutionContext}) for the partition elements are provided by the
	 * {@link StepExecutionSplitter}.
	 *
	 * @param stepExecution the master step execution for the partition
	 *
	 * @see Step#execute(StepExecution)
	 */
	@Override
	protected void doExecute(StepExecution stepExecution) throws Exception {

		if(hasReducer) {
			reducer.beginPartitionedStep();
		}

		// Wait for task completion and then aggregate the results
		Collection<StepExecution> stepExecutions = getPartitionHandler().handle(null, stepExecution);
		stepExecution.upgradeStatus(BatchStatus.COMPLETED);
		stepExecutionAggregator.aggregate(stepExecution, stepExecutions);

		if (stepExecution.getStatus().isUnsuccessful()) {
			if (hasReducer) {
				reducer.rollbackPartitionedStep();
				reducer.afterPartitionedStepCompletion(PartitionStatus.ROLLBACK);
			}
			throw new JobExecutionException("Partition handler returned an unsuccessful step");
		}

		if (hasReducer) {
			reducer.beforePartitionedStepCompletion();
			reducer.afterPartitionedStepCompletion(PartitionStatus.COMMIT);
		}
	}
}
