package org.embulk.spi; import java.util.List; import java.util.ArrayList; import org.embulk.config.Task; import org.embulk.config.TaskSource; import org.embulk.config.ConfigSource; import org.embulk.config.ConfigDiff; import org.embulk.config.TaskReport; import org.embulk.config.Config; import org.embulk.config.ConfigDefault; import org.embulk.plugin.PluginType; import org.embulk.plugin.compat.PluginWrappers; import org.embulk.spi.util.Encoders; public class FileOutputRunner implements OutputPlugin { private final FileOutputPlugin fileOutputPlugin; public FileOutputRunner(FileOutputPlugin fileOutputPlugin) { this.fileOutputPlugin = fileOutputPlugin; } private interface RunnerTask extends Task { @Config("type") public PluginType getType(); @Config("encoders") @ConfigDefault("[]") public List<ConfigSource> getEncoderConfigs(); @Config("formatter") public ConfigSource getFormatterConfig(); public void setFileOutputTaskSource(TaskSource v); public TaskSource getFileOutputTaskSource(); public void setEncoderTaskSources(List<TaskSource> v); public List<TaskSource> getEncoderTaskSources(); public void setFormatterTaskSource(TaskSource v); public TaskSource getFormatterTaskSource(); } protected List<EncoderPlugin> newEncoderPlugins(RunnerTask task) { return Encoders.newEncoderPlugins(Exec.session(), task.getEncoderConfigs()); } protected FormatterPlugin newFormatterPlugin(RunnerTask task) { return Exec.newPlugin(FormatterPlugin.class, task.getFormatterConfig().get(PluginType.class, "type")); } @Override public ConfigDiff transaction(ConfigSource config, final Schema schema, final int taskCount, final OutputPlugin.Control control) { final RunnerTask task = config.loadConfig(RunnerTask.class); return fileOutputPlugin.transaction(config, taskCount, new RunnerControl(schema, task, control)); } public ConfigDiff resume(TaskSource taskSource, Schema schema, int taskCount, final OutputPlugin.Control control) { final RunnerTask task = taskSource.loadTask(RunnerTask.class); return fileOutputPlugin.resume(task.getFileOutputTaskSource(), taskCount, new RunnerControl(schema, task, control)); } private class RunnerControl implements FileOutputPlugin.Control { private final Schema schema; private final RunnerTask task; private final List<EncoderPlugin> encoderPlugins; private final FormatterPlugin formatterPlugin; private final OutputPlugin.Control nextControl; public RunnerControl(Schema schema, RunnerTask task, OutputPlugin.Control nextControl) { this.schema = schema; this.task = task; // create plugins earlier than run() to throw exceptions early this.encoderPlugins = newEncoderPlugins(task); this.formatterPlugin = newFormatterPlugin(task); this.nextControl = nextControl; } @Override public List<TaskReport> run(final TaskSource fileOutputTaskSource) { final List<TaskReport> taskReports = new ArrayList<TaskReport>(); Encoders.transaction(encoderPlugins, task.getEncoderConfigs(), new Encoders.Control() { public void run(final List<TaskSource> encoderTaskSources) { formatterPlugin.transaction(task.getFormatterConfig(), schema, new FormatterPlugin.Control() { public void run(final TaskSource formatterTaskSource) { task.setFileOutputTaskSource(fileOutputTaskSource); task.setEncoderTaskSources(encoderTaskSources); task.setFormatterTaskSource(formatterTaskSource); taskReports.addAll(nextControl.run(task.dump())); } }); } }); return taskReports; } } public void cleanup(TaskSource taskSource, Schema schema, int taskCount, List<TaskReport> successtaskReports) { fileOutputPlugin.cleanup(taskSource, taskCount, successtaskReports); } @Override public TransactionalPageOutput open(TaskSource taskSource, Schema schema, int taskIndex) { final RunnerTask task = taskSource.loadTask(RunnerTask.class); List<EncoderPlugin> encoderPlugins = newEncoderPlugins(task); FormatterPlugin formatterPlugin = newFormatterPlugin(task); try (AbortTransactionResource aborter = new AbortTransactionResource()) { try (CloseResource closer = new CloseResource()) { TransactionalFileOutput finalOutput = PluginWrappers.transactionalFileOutput( fileOutputPlugin.open(task.getFileOutputTaskSource(), taskIndex)); aborter.abortThis(finalOutput); closer.closeThis(finalOutput); FileOutput encodedOutput = Encoders.open(encoderPlugins, task.getEncoderTaskSources(), finalOutput); closer.closeThis(encodedOutput); PageOutput output = formatterPlugin.open(task.getFormatterTaskSource(), schema, encodedOutput); closer.closeThis(output); TransactionalPageOutput ret = new DelegateTransactionalPageOutput(finalOutput, output); aborter.dontAbort(); closer.dontClose(); // ownership of output is transferred to caller (input plugin). the owner will close output. return ret; } } } private static class DelegateTransactionalPageOutput implements TransactionalPageOutput { private final Transactional tran; private final PageOutput output; public DelegateTransactionalPageOutput(Transactional tran, PageOutput output) { this.tran = tran; this.output = output; } @Override public void add(Page page) { output.add(page); } @Override public void finish() { output.finish(); } @Override public void close() { output.close(); } @Override public void abort() { tran.abort(); } @Override public TaskReport commit() { // TODO check finished return tran.commit(); } } }