Java tutorial
/* * Copyright (C) 2010 Herve Quiroz * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.trancecode.xproc.step; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.io.Closeables; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.XdmNode; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.trancecode.io.TcByteStreams; import org.trancecode.lang.StringPredicates; import org.trancecode.lang.TcStrings; import org.trancecode.logging.Logger; import org.trancecode.xml.saxon.SaxonAxis; import org.trancecode.xml.saxon.SaxonBuilder; import org.trancecode.xproc.XProcExceptions; import org.trancecode.xproc.XProcXmlModel; import org.trancecode.xproc.port.XProcPorts; import org.trancecode.xproc.variable.XProcOptions; /** * Step processor for the p:exec optional XProc step. * * @author Herve Quiroz * @see <a href="http://www.w3.org/TR/xproc/#c.exec">p:exec</a> */ @ExternalResources(read = true, write = true) public final class ExecStepProcessor extends AbstractStepProcessor { private static final Logger LOG = Logger.getLogger(ExecStepProcessor.class); @Override public QName getStepType() { return XProcSteps.EXEC; } @Override protected void execute(final StepInput input, final StepOutput output) throws Exception { final String pathSeparator = input.getOptionValue(XProcOptions.PATH_SEPARATOR); final String command; if (pathSeparator != null) { command = input.getOptionValue(XProcOptions.COMMAND).replace(pathSeparator, File.separator); } else { command = input.getOptionValue(XProcOptions.COMMAND); } final String argSeparator = input.getOptionValue(XProcOptions.ARG_SEPARATOR, " "); final Iterable<String> rawArgs = TcStrings.split(input.getOptionValue(XProcOptions.ARGS), argSeparator); final Iterable<String> args = Iterables.transform(rawArgs, arg -> { if (pathSeparator != null) { return arg.replace(pathSeparator, File.separator); } return arg; }); final String cwd = input.getOptionValue(XProcOptions.CWD); final List<XdmNode> inputDocuments = ImmutableList.copyOf(input.readNodes(XProcPorts.SOURCE)); if (inputDocuments.size() > 1) { throw XProcExceptions.xd0006(input.getStep().getLocation(), input.getStep().getPortReference(XProcPorts.SOURCE)); } final boolean sourceIsXml = Boolean.parseBoolean(input.getOptionValue(XProcOptions.SOURCE_IS_XML)); final boolean resultIsXml = Boolean.parseBoolean(input.getOptionValue(XProcOptions.RESULT_IS_XML)); final boolean wrapResultLines = Boolean.parseBoolean(input.getOptionValue(XProcOptions.WRAP_RESULT_LINES)); final boolean errorsIsXml = Boolean.parseBoolean(input.getOptionValue(XProcOptions.ERRORS_IS_XML)); final boolean wrapErrorLines = Boolean.parseBoolean(input.getOptionValue(XProcOptions.WRAP_ERROR_LINES)); if ((resultIsXml && wrapResultLines) || (errorsIsXml && wrapErrorLines)) { throw XProcExceptions.xc0035(input.getStep().getLocation()); } final List<String> commandLine = Lists.newArrayList(); commandLine.add(command); Iterables.addAll(commandLine, Iterables.filter(args, StringPredicates.isNotEmpty())); LOG.trace(" commandLine = {}", commandLine); final ProcessBuilder processBuilder = new ProcessBuilder(commandLine.toArray(new String[0])); processBuilder.redirectErrorStream(false); if (cwd != null) { final Path newDirectory = Paths.get(cwd); if (!Files.isDirectory(newDirectory)) { throw XProcExceptions.xc0034(input.getLocation()); } processBuilder.directory(new File(cwd)); } final Process process; try { process = processBuilder.start(); } catch (IOException e) { throw XProcExceptions.xc0033(input.getLocation()); } if (!inputDocuments.isEmpty()) { final String inputContent; if (sourceIsXml) { inputContent = inputDocuments.get(0).toString(); } else { inputContent = SaxonAxis.childElement(inputDocuments.get(0)).getStringValue(); } new Thread(() -> { try { IOUtils.write(inputContent, process.getOutputStream()); } catch (final IOException e) { throw new IllegalStateException(e); } finally { Closeables.closeQuietly(process.getOutputStream()); } }).start(); } final Supplier<File> stdout = TcByteStreams.copyToTempFile(process.getInputStream()); final Supplier<File> stderr = TcByteStreams.copyToTempFile(process.getErrorStream()); final int exitCode = process.waitFor(); LOG.trace("exitCode = {}", exitCode); final String failureThreshold = input.getOptionValue(XProcOptions.FAILURE_THRESHOLD); if (failureThreshold != null) { LOG.trace("failureThreshold = {}", failureThreshold); final int numericFailureThreshold = Integer.parseInt(failureThreshold); if (exitCode > numericFailureThreshold) { throw XProcExceptions.xc0064(input.getLocation(), exitCode, numericFailureThreshold); } } final File stdoutFile = stdout.get(); final File stderrFile = stderr.get(); process.destroy(); final Processor processor = input.getPipelineContext().getProcessor(); output.writeNodes(XProcPorts.RESULT, parseOutput(stdoutFile, resultIsXml, wrapResultLines, input.getStep().getNode(), processor)); output.writeNodes(XProcPorts.ERRORS, parseOutput(stderrFile, errorsIsXml, wrapErrorLines, input.getStep().getNode(), processor)); output.writeNodes(XProcPorts.EXIT_STATUS, input.newResultElement(Integer.toString(exitCode))); } private static XdmNode parseOutput(final File file, final boolean outputIsXml, final boolean wrapLines, final XdmNode namespaceContext, final Processor processor) throws Exception { final SaxonBuilder builder = new SaxonBuilder(processor.getUnderlyingConfiguration()); builder.startDocument(); builder.startElement(XProcXmlModel.Elements.RESULT, namespaceContext); if (file.length() > 0) { if (outputIsXml) { final XdmNode resultNode = processor.newDocumentBuilder().build(file); builder.nodes(SaxonAxis.childElement(resultNode)); } else { if (wrapLines) { @SuppressWarnings("unchecked") final List<String> lines = FileUtils.readLines(file); for (final String line : lines) { builder.startElement(XProcXmlModel.Elements.LINE); builder.text(line); builder.endElement(); } } else { builder.text(FileUtils.readFileToString(file)); } } } builder.endElement(); builder.endDocument(); return builder.getNode(); } }