/*
* Copyright © 2015 Cask Data, Inc.
*
* 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 co.cask.cdap.etl.common;
import co.cask.cdap.etl.api.Destroyable;
import co.cask.cdap.etl.api.InvalidEntry;
import co.cask.cdap.etl.api.Transformation;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Executes Transforms one iteration at a time, tracking how many records were input into and output from
* each transform.
*
* @param <IN> the type of input object to the first transform
*
*/
public class TransformExecutor<IN> implements Destroyable {
private final Set<String> startingPoints;
private final Map<String, TransformDetail> transformDetailMap;
public TransformExecutor(Map<String, TransformDetail> transformDetailMap, Set<String> startingPoints) {
this.transformDetailMap = transformDetailMap;
this.startingPoints = startingPoints;
}
public TransformResponse runOneIteration(IN input) throws Exception {
for (String stageName : startingPoints) {
executeTransformation(stageName, ImmutableList.of(input));
}
Map<String, Collection<Object>> terminalNodeEntriesMap = new HashMap<>();
Map<String, Collection<InvalidEntry<Object>>> errors = new HashMap<>();
for (Map.Entry<String, TransformDetail> transformDetailEntry : transformDetailMap.entrySet()) {
if (transformDetailEntry.getValue().getNextStages().isEmpty()) {
// terminal node
Collection<Object> entries = transformDetailEntry.getValue().getEntries();
if (entries != null) {
terminalNodeEntriesMap.put(transformDetailEntry.getKey(), entries);
}
}
if (!transformDetailEntry.getValue().getErrors().isEmpty()) {
errors.put(transformDetailEntry.getKey(), transformDetailEntry.getValue().getErrors());
}
}
return new TransformResponse(terminalNodeEntriesMap, errors);
}
private <T> void executeTransformation(final String stageName, Collection<T> input) throws Exception {
if (input == null) {
return;
}
TransformDetail transformDetail = transformDetailMap.get(stageName);
Transformation<T, Object> transformation = transformDetail.getTransformation();
// clear old data for this stageName if its not a terminal node
if (!transformDetail.getNextStages().isEmpty()) {
transformDetail.getEntries().clear();
}
for (T inputEntry : input) {
transformation.transform(inputEntry, transformDetail);
}
for (String nextStage : transformDetail.getNextStages()) {
executeTransformation(nextStage, transformDetail.getEntries());
}
}
public void resetEmitter() {
for (TransformDetail transformDetailEntry : transformDetailMap.values()) {
transformDetailEntry.resetEmitter();
}
}
@Override
public void destroy() {
for (TransformDetail transformDetailEntry : transformDetailMap.values()) {
transformDetailEntry.destroy();
}
}
}