org.geoserver.ogr.core.AbstractToolWrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.ogr.core.AbstractToolWrapper.java

Source

/* (c) 2015 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.ogr.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * Base class for helpers used to invoke an external tool.
 * 
 * @author Andrea Aime, GeoSolutions
 * @author Stefano Costa, GeoSolutions
 */
public abstract class AbstractToolWrapper implements ToolWrapper {

    private String executable;
    private Map<String, String> environment;

    public AbstractToolWrapper(String executable, Map<String, String> environment) {
        this.executable = executable;
        this.environment = new HashMap<String, String>();
        if (environment != null) {
            this.environment.putAll(environment);
        }
    }

    @Override
    public String getExecutable() {
        return executable;
    }

    @Override
    public Map<String, String> getEnvironment() {
        return new HashMap<String, String>(environment);
    }

    @Override
    public boolean isInputFirst() {
        return true;
    }

    @Override
    public File convert(File inputData, File outputDirectory, String typeName, Format format,
            CoordinateReferenceSystem crs) throws IOException, InterruptedException {
        // build the command line
        List<String> cmd = new ArrayList<String>();
        cmd.add(executable);

        String toolFormatParameter = getToolFormatParameter();
        if (toolFormatParameter != null) {
            cmd.add(toolFormatParameter);
            cmd.add(format.getToolFormat());
        }

        if (format.getOptions() != null) {
            for (String option : format.getOptions()) {
                cmd.add(option);
            }
        }

        StringBuilder sb = new StringBuilder();
        String outFileName = null;
        int exitCode = -1;
        try {
            onBeforeRun(cmd, inputData, outputDirectory, typeName, format, crs);

            outFileName = setInputOutput(cmd, inputData, outputDirectory, typeName, format);

            exitCode = run(cmd, sb);
        } finally {
            onAfterRun(exitCode);
        }

        if (exitCode != 0)
            throw new IOException(executable + " did not terminate successfully, exit code " + exitCode
                    + ". Was trying to run: " + cmd + "\nResulted in:\n" + sb);

        // output may be a directory, handle that case gracefully
        File output = new File(outputDirectory, outFileName);
        if (output.isDirectory()) {
            output = new File(output, outFileName);
        }
        return output;
    }

    /**
     * Sets up input and output parameters.
     * 
     * <p>
     * Uses {@link #isInputFirst()} internally to determine whether input or output should come first in the list of arguments.
     * </p>
     * 
     * <p>
     * May be overridden by subclasses, e.g. to support commands that modify the input file inline and thus need no output parameter.
     * </p>
     * 
     * @param cmd the command to run and its arguments
     * @param inputData the input file
     * @param outputDirectory the output directory
     * @param typeName the type name
     * @param format the format descriptor
     * @return the name of the (main) output file
     */
    protected String setInputOutput(List<String> cmd, File inputData, File outputDirectory, String typeName,
            Format format) {
        String outFileName = typeName;

        if (format.getFileExtension() != null)
            outFileName += format.getFileExtension();
        if (isInputFirst()) {
            cmd.add(inputData.getAbsolutePath());
            cmd.add(new File(outputDirectory, outFileName).getAbsolutePath());
        } else {
            cmd.add(new File(outputDirectory, outFileName).getAbsolutePath());
            cmd.add(inputData.getAbsolutePath());
        }

        return outFileName;
    }

    /**
     * Utility method to dump a {@link CoordinateReferenceSystem} to a temporary file on disk.
     * 
     * @param parentDir
     * @param crs
     * @return the temp file containing the CRS definition in WKT format
     * @throws IOException 
     */
    protected static File dumpCrs(File parentDir, CoordinateReferenceSystem crs) throws IOException {
        File crsFile = null;
        if (crs != null) {
            // we don't use an EPSG code since there is no guarantee we'll be able to reverse
            // engineer one. Using WKT also ensures the EPSG params such as the TOWGS84 ones are
            // not lost in the conversion
            // We also write to a file because some operating systems cannot take arguments with
            // quotes and spaces inside (and/or ProcessBuilder is not good enough to escape them)
            crsFile = File.createTempFile("srs", "wkt", parentDir);
            String s = crs.toWKT();
            s = s.replaceAll("\n\r", "").replaceAll("  ", "");
            FileUtils.writeStringToFile(crsFile, s);
        }

        return crsFile;
    }

    /**
     * Invoked by <code>convert()</code> before the command is actually run, but after the options specified in the format configuration have been
     * added to the arguments list.
     * 
     * <p>
     * Default implementation does nothing at all. May be implemented by subclasses to append additional arguments to <code>cmd</code>.
     * </p>
     * 
     * @param cmd the command to run and its arguments
     * @param inputData
     * @param outputDirectory
     * @param typeName
     * @param format
     * @param crs
     * @throws IOException
     */
    protected void onBeforeRun(List<String> cmd, File inputData, File outputDirectory, String typeName,
            Format format, CoordinateReferenceSystem crs) throws IOException {
        // default implementation does nothing
    }

    /**
     * Invoked by <code>convert()</code> after the command is run. Invocation is done inside a <code>try ... finally</code> block, so it happens even
     * if an exception is thrown during command execution.
     * 
     * <p>
     * Default implementation does nothing at all. May be implemented by subclasses to do some clean-up work.
     * </p>
     * 
     * @param exitCode the exit code of the invoked command. Usually, 0 indicates normal termination
     * @throws IOException
     */
    protected void onAfterRun(int exitCode) throws IOException {
        // default implementation does nothing
    }

    /**
     * Runs the specified command appending the output to the string builder and
     * returning the exit code.
     * 
     * @param cmd the command to run and its arguments
     * @param sb command output is appended here
     * @return the exit code of the invoked command. Usually, 0 indicates normal termination
     * @throws IOException
     * @throws InterruptedException
     */
    protected int run(List<String> cmd, StringBuilder sb) throws IOException, InterruptedException {
        // run the process and grab the output for error reporting purposes
        ProcessBuilder builder = new ProcessBuilder(cmd);
        if (environment != null)
            builder.environment().putAll(environment);
        builder.redirectErrorStream(true);
        Process p = builder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line = null;
        while ((line = reader.readLine()) != null) {
            if (sb != null) {
                sb.append("\n");
                sb.append(line);
            }
        }
        return p.waitFor();
    }

}