// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.core.pipeline.common; import java.util.HashMap; import java.util.Map; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.task.common.Task; /** * All task instances within a pipeline are managed by a task manager. This * manager is not responsible for the creation of the task, but it is * responsible for connecting it to its input and output tasks and responsible * for managing the invocation of the task. * * @author Brett Henderson */ public abstract class TaskManager { private String taskId; private Map<Integer, String> inputPipeNames; private Map<Integer, String> outputPipeNames; /** * Creates a new instance. * * @param taskId * A unique identifier for the task. This is used to produce * meaningful errors when errors occur. * @param pipeArgs * The arguments defining input and output pipes for the task, * pipes are a logical concept for identifying how the tasks are * connected together. */ protected TaskManager(String taskId, Map<String, String> pipeArgs) { this.taskId = taskId; inputPipeNames = buildPipes(pipeArgs, true); outputPipeNames = buildPipes(pipeArgs, false); } /** * Returns the index of a pipe based upon its suffix. * * @param pipeArgNameSuffix * The suffix of the pipe argument name. * @return The index of the pipe being referred to by the pipe argument. */ private int getPipeIndex(String pipeArgNameSuffix) { int pipeIndex; // If there is no suffix, then it is the default pipe 0. // Otherwise we must check the suffix. if (pipeArgNameSuffix.length() <= 0) { pipeIndex = 0; } else { String indexString; // Validate that the suffix begins with a '.' character. if (pipeArgNameSuffix.indexOf('.') != 0) { throw new OsmosisRuntimeException( "Task " + taskId + " contains a pipe definition without '.' between prefix and suffix." ); } // The remaining suffix must be a number defining the index. indexString = pipeArgNameSuffix.substring(1); // Ensure the index exists. if (indexString.length() <= 0) { throw new OsmosisRuntimeException( "Task " + taskId + " contains a pipe definition without an index after the '.'." ); } // Parse the suffix string into a number. try { pipeIndex = Integer.parseInt(indexString); } catch (NumberFormatException e) { throw new OsmosisRuntimeException("Task " + taskId + " has a pipe with an incorrect index suffix."); } } return pipeIndex; } /** * Builds a list of pipe names keyed by their specified index. * * @param pipeArgs * The task pipe arguments. * @param buildInputPipes * If true will build the list of input pipes, else output pipes. * @return The list of pipe argument values. */ private Map<Integer, String> buildPipes(Map<String, String> pipeArgs, boolean buildInputPipes) { String pipeArgumentPrefix; String pipeType; Map<Integer, String> pipes; pipes = new HashMap<Integer, String>(); // Setup processing variables based on whether we're building input or // output pipes. if (buildInputPipes) { pipeArgumentPrefix = PipelineConstants.IN_PIPE_ARGUMENT_PREFIX; pipeType = "input"; } else { pipeArgumentPrefix = PipelineConstants.OUT_PIPE_ARGUMENT_PREFIX; pipeType = "output"; } // Iterate through all the pipes searching for the required pipe definitions. for (String pipeArgName : pipeArgs.keySet()) { // It is an input pipe definition if starts with the input pipe prefix. if (pipeArgName.indexOf(pipeArgumentPrefix) == 0) { Integer pipeIndex; pipeIndex = new Integer( getPipeIndex(pipeArgName.substring(pipeArgumentPrefix.length())) ); // Ensure that there aren't two pipes with the same index. if (pipes.containsKey(pipeIndex)) { throw new OsmosisRuntimeException( "Task " + taskId + " has a duplicate " + pipeType + " pipe with index " + pipeIndex + "."); } // The pipe is valid, so add it to the pipe map keyed by its index. pipes.put(pipeIndex, pipeArgs.get(pipeArgName)); } } return pipes; } /** * Finds the specified pipe task, unregisters it from the available tasks * and returns it. * * @param pipeTasks * The currently registered pipe tasks. * @param pipeIndex * The input pipe index for the current task. * @param requiredTaskType * The required type of the input task. * @return The task to be used as input at the specified index. */ protected Task getInputTask(PipeTasks pipeTasks, int pipeIndex, Class<? extends Task> requiredTaskType) { Task inputTask; Integer pipeIndexO = new Integer(pipeIndex); // We use the specified pipe name if it exists, otherwise we get the // next available default pipe. if (inputPipeNames.containsKey(pipeIndexO)) { inputTask = pipeTasks.retrieveTask(taskId, inputPipeNames.get(pipeIndexO), requiredTaskType); } else { inputTask = pipeTasks.retrieveTask(taskId, requiredTaskType); } return inputTask; } /** * Registers the specified task as an output. * * @param pipeTasks * The currently registered pipe tasks. * @param outputTask * The task to be registered. * @param pipeIndex * The index of the pipe on the current task. */ protected void setOutputTask(PipeTasks pipeTasks, Task outputTask, int pipeIndex) { Integer pipeIndexO = new Integer(pipeIndex); // We use the specified pipe name if it exists, otherwise we register // using the next available default pipe name. if (outputPipeNames.containsKey(pipeIndexO)) { pipeTasks.putTask(taskId, outputPipeNames.get(pipeIndexO), outputTask); } else { pipeTasks.putTask(taskId, outputTask); } } /** * @return The taskId. */ protected String getTaskId() { return taskId; } /** * Connects the task to any input tasks based upon the pipes created by * source tasks, and makes any output pipes available to be used by * subsequent sink tasks. * * @param pipeTasks * The currently registered pipe tasks. This will be modified to * remove any consumed inputs, and modified to add new outputs. */ public abstract void connect(PipeTasks pipeTasks); /** * Begins execution of the task. For many sink tasks, this will not do * anything. Source tasks are likely to begin execution within a new thread. */ public abstract void execute(); /** * Waits until all tasks have completed execution before returning. This is * intended for source tasks that run within a separate thread, sink tasks * will not do anything here. * * @return True if the thread completed successfully. */ public abstract boolean waitForCompletion(); }