eu.planets_project.services.migration.kakadu.KakaduCompressMigration.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.services.migration.kakadu.KakaduCompressMigration.java

Source

/*
 *
 */
package eu.planets_project.services.migration.kakadu;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;

import javax.jws.WebService;
import javax.resource.spi.IllegalStateException;
import javax.xml.ws.soap.MTOM;

import org.apache.commons.io.FileUtils;

import com.sun.xml.ws.developer.StreamingAttachment;

import eu.planets_project.ifr.core.techreg.formats.FormatRegistry;
import eu.planets_project.ifr.core.techreg.formats.FormatRegistryFactory;
import eu.planets_project.ifr.core.techreg.properties.ServiceProperties;
import eu.planets_project.services.PlanetsServices;
import eu.planets_project.services.datatypes.Content;
import eu.planets_project.services.datatypes.DigitalObject;
import eu.planets_project.services.datatypes.MigrationPath;
import eu.planets_project.services.datatypes.Parameter;
import eu.planets_project.services.datatypes.Property;
import eu.planets_project.services.datatypes.ServiceDescription;
import eu.planets_project.services.datatypes.ServiceReport;
import eu.planets_project.services.datatypes.ServiceReport.Status;
import eu.planets_project.services.datatypes.ServiceReport.Type;
import eu.planets_project.services.migrate.Migrate;
import eu.planets_project.services.migrate.MigrateResult;
import eu.planets_project.services.utils.DigitalObjectUtils;
import eu.planets_project.services.utils.ProcessRunner;
import eu.planets_project.services.utils.ServicePerformanceHelper;
import eu.planets_project.services.utils.ServiceUtils;

/**
 * The KakaduCompressMigration migrates TIF files to JP2 files.
 * @author Sven Schlarb <shsschlarb-planets@yahoo.de>
 */
@WebService(name = KakaduCompressMigration.NAME, serviceName = Migrate.NAME, targetNamespace = PlanetsServices.NS, endpointInterface = "eu.planets_project.services.migrate.Migrate")
@MTOM
@StreamingAttachment(parseEagerly = true, memoryThreshold = ServiceUtils.JAXWS_SIZE_THRESHOLD)
public final class KakaduCompressMigration implements Migrate {

    Logger log = Logger.getLogger(KakaduCompressMigration.class.getName());
    /** The dvi ps installation dir */
    public String kakadu_install_dir;
    /** The kakadu application name */
    public String kakadu_app_name;
    /** The output file extension */
    // public String kakadu_outfile_ext;
    private File tmpInFile;
    private File tmpOutFile;
    String inputFmtExt = null;
    String outputFmtExt = null;
    /***/
    static final String NAME = "KakaduCompressMigration";
    List<String> inputFormats = null;
    List<String> outputFormats = null;
    HashMap<String, String> formatMapping = null;
    List<Parameter> serviceParametersList;
    List<Parameter> requestParametersList;
    StringBuffer serviceMessage = null;
    KakaduCompressServiceParameters kduServiceParameters = null;

    /***/
    private static final long serialVersionUID = 2127494848765937613L;

    private void init() {
        // input formats
        inputFormats = new ArrayList<String>();
        // TIF, RAW, BMP, PBM, PGM and PPM
        inputFormats.add("tif");
        inputFormats.add("raw");
        inputFormats.add("bmp");
        inputFormats.add("pbm");
        inputFormats.add("pgm");
        inputFormats.add("ppm");

        // output formats and associated output parameters
        outputFormats = new ArrayList<String>();
        outputFormats.add("jp2");

        // Disambiguation of extensions, e.g. {"JPG","JPEG"} to {"JPEG"}
        // FIXIT This should be supported by the FormatRegistryImpl class, but
        // it does not provide the complete set at the moment.
        formatMapping = new HashMap<String, String>();
        formatMapping.put("tiff", "tif");
        serviceMessage = new StringBuffer();

        kduServiceParameters = new KakaduCompressServiceParameters();

    }

    /**
     * Initialize the parameters list for all migration file formats. Every
     * parameter has a default value which is overridden where requested
     * from the user (parameters contains the parameters passed to the
     * service by the user).
     */
    private void initParameters() {
        serviceParametersList = KakaduCompressServiceParameters.getParameterList();
    }

    /**
     * {@inheritDoc}
     * @see eu.planets_project.services.migrate.Migrate#migrate(eu.planets_project.services.datatypes.DigitalObject,
     *      java.net.URI, java.net.URI,
     *      eu.planets_project.services.datatypes.Parameter)
     */
    public MigrateResult migrate(final DigitalObject digitalObject, URI inputFormat, URI outputFormat,
            List<Parameter> parameters) {

        // Start timing...
        ServicePerformanceHelper sph = new ServicePerformanceHelper();

        requestParametersList = parameters;
        Properties props = new Properties();
        try {

            String strRsc = "/eu/planets_project/services/migration/kakadu/kakadu.properties";
            props.load(this.getClass().getResourceAsStream(strRsc));
            // config vars
            this.kakadu_install_dir = props.getProperty("kakadu.install.dir");
            this.kakadu_app_name = props.getProperty("kakadu.app.name");

        } catch (Exception e) {
            // // config vars
            this.kakadu_install_dir = "C:/Programme/Kakadu";
            this.kakadu_app_name = "kdu_compress";
        }
        log.info("Using kakadu install directory: " + this.kakadu_install_dir);
        log.info("Using kakadu application name: " + this.kakadu_app_name);

        init();
        // Initialise parameters with default values
        initParameters();
        getExtensions(inputFormat, outputFormat);

        /*
         * We just return a new digital object with the same required arguments
         * as the given:
         */
        byte[] binary = null;
        InputStream inputStream = digitalObject.getContent().getInputStream();

        // write input object to temporary file
        tmpInFile = DigitalObjectUtils.toFile(digitalObject); //TODO need extension?

        // Record time take to load the input data:
        sph.loaded();

        if (!(tmpInFile.exists() && tmpInFile.isFile() && tmpInFile.canRead())) {
            log.severe("Unable to create temporary input file!");
            return null;
        }
        log.info("Temporary input file created: " + tmpInFile.getAbsolutePath());

        // outfile name
        String outFileStr = tmpInFile.getAbsolutePath().replaceAll(inputFmtExt, "jp2");

        log.info("Output file name: " + outFileStr);

        StringBuffer serviceReportMessage = new StringBuffer();
        // run command
        ProcessRunner runner = new ProcessRunner();
        List<String> command = new ArrayList<String>();
        // setting up command
        // -i source.tif -o destination.jp2 Creversible=yes -rate -,1,0.5,0.25 Clevels=5
        command.add(this.kakadu_app_name);
        command.add("-i");
        command.add(tmpInFile.getAbsolutePath());
        command.add("-o");
        command.add(outFileStr);
        for (Parameter requestParm : requestParametersList) {
            ServiceParameter servParm = kduServiceParameters.getParameter(requestParm.getName());
            if (servParm != null) {
                servParm.setRequestValue(requestParm.getValue());
                if (servParm.isValid())
                    command.addAll(servParm.getCommandListItems());
                else
                    serviceReportMessage.append(servParm.getStatusMessage());
            } else
                serviceReportMessage.append(
                        "Parameter skipped: Service does not support parameter '" + requestParm.getName() + "'. ");
        }

        runner.setCommand(command);
        runner.setInputStream(inputStream);
        //timeout after 10 minutes, e.g. the tool crashed
        runner.setTimeout(600000);
        log.info("Executing command (update): " + command.toString() + " ...");

        long startMillis = System.currentTimeMillis();
        runner.run();
        long endMillis = System.currentTimeMillis();
        int return_code = runner.getReturnCode();
        if (return_code != 0) {
            log.severe("Kakadu conversion error code: " + Integer.toString(return_code));
            log.severe(runner.getProcessErrorAsString());
            return null;
        }
        if (return_code == -1) {
            //in this case the time-out occurred 
            ServiceReport report = new ServiceReport(Type.ERROR, Status.TOOL_ERROR,
                    "process runner time-out occurred after 10 minutes of tool unresponsiveness");
            return new MigrateResult(null, report);
        }

        tmpOutFile = new File(outFileStr);
        ServiceReport report;

        // read byte array from temporary file
        if (tmpOutFile.isFile() && tmpOutFile.canRead()) {
            try {
                binary = FileUtils.readFileToByteArray(tmpOutFile);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //get the measured proeprties to return
            List<Property> retProps = sph.getPerformanceProperties();
            retProps.add(ServiceProperties.createToolRunnerTimeProperty(endMillis - startMillis));

            report = new ServiceReport(Type.INFO, Status.SUCCESS,
                    serviceReportMessage.toString() + "Wrote: " + tmpOutFile, retProps);
        } else {
            String message = "Error: Unable to read temporary file " + tmpOutFile.getAbsolutePath();
            log.severe(message);
            report = new ServiceReport(Type.ERROR, Status.INSTALLATION_ERROR,
                    serviceReportMessage.toString() + message);
        }

        //build the returning digital object
        DigitalObject newDO = null;
        newDO = new DigitalObject.Builder(Content.byValue(binary)).build();

        return new MigrateResult(newDO, report);
    }

    /**
     * Get value of one parameter.
     *
     * @param ext Extension for which we retrieve the parameter list.
     * @return Paramter string
     */
    private String getRequestParameterValue(String parmName) {
        Iterator<Parameter> itr = this.requestParametersList.iterator();
        String paramValue = null;
        while (itr.hasNext()) {
            Parameter param = (Parameter) itr.next();
            if (param.getName() != null && param.getName().equals(parmName)) {
                paramValue = param.getValue();
                return paramValue;
            }
        }
        return null;
    }

    private void getExtensions(URI inputFormat, URI outputFormat) {
        if (inputFormat != null && outputFormat != null) {
            inputFmtExt = getFormatExt(inputFormat, false);
            outputFmtExt = getFormatExt(outputFormat, true);
        }
    }

    /**
     * Gets one extension from a set of possible extensions for the incoming
     * request planets URI (e.g. planets:fmt/ext/jpeg) which matches with one
     * format of the set of kakadu's supported input/output formats. If
     * isOutput is false, it checks against the gimp input formats ArrayList,
     * otherwise it checks against the gimp output formats HashMap.
     * @param formatUri Planets URI (e.g. planets:fmt/ext/jpeg)
     * @param isOutput Is the format an input or an output format
     * @return Format extension (e.g. "JPEG")
     */
    private String getFormatExt(URI formatUri, boolean isOutput) {
        String fmtStr = null;
        // status variable which indicates if an input/out format has been found
        // while iterating over possible matches
        boolean fmtFound = false;
        // Extensions which correspond to the format
        // planets:fmt/ext/jpg -> { "JPEG", "JPG" }
        // or can be found in the list of supported formats
        Set<String> reqInputFormatExts = FormatRegistryFactory.getFormatRegistry().getExtensions(formatUri);
        Iterator<String> itrReq = reqInputFormatExts.iterator();
        // Iterate either over input formats ArrayList or over output formats
        // HasMap
        Iterator<String> itrJasper = (isOutput) ? outputFormats.iterator() : inputFormats.iterator();
        // Iterate over possible extensions that correspond to the request
        // planets uri.
        while (itrReq.hasNext()) {
            // Iterate over the different extensions of the planets:fmt/ext/jpg
            // format URI, note that the relation of Planets-format-URI to
            // extensions is 1 : n.
            String reqFmtExt = normalizeExt((String) itrReq.next());
            while (itrJasper.hasNext()) {
                // Iterate over the formats that kakadu offers either as input
                // or
                // as output format.
                // See input formats in the this.init() method to see the
                // kakadu input/output formats offered by this service.
                String gimpFmtStr = (String) itrJasper.next();
                if (reqFmtExt.equalsIgnoreCase(gimpFmtStr)) {
                    // select the gimp supported format
                    fmtStr = gimpFmtStr;
                    fmtFound = true;
                    break;
                }
                if (fmtFound) {
                    break;
                }
            }
        }
        return fmtStr;
    }

    /**
     * Disambiguation (e.g. TIFF -> TIF) according to the formatMapping datas
     * structure defined in this class.
     * @param ext
     * @return Uppercase disambiguized extension string
     */
    private String normalizeExt(String ext) {
        String normExt = ext.toUpperCase();
        return ((formatMapping.containsKey(normExt)) ? (String) formatMapping.get(normExt) : normExt);
    }

    /**
     * @see eu.planets_project.services.migrate.Migrate#describe()
     */
    public ServiceDescription describe() {
        init();
        initParameters();
        List<Parameter> parameters = serviceParametersList;
        ServiceDescription.Builder builder = new ServiceDescription.Builder(NAME, Migrate.class.getName());
        builder.author("Sven Schlarb <shsschlarb-planets@yahoo.de>");
        builder.classname(this.getClass().getCanonicalName());
        builder.description("Kakadu Version 6.2.1 JPEG2000 sample command line"
                + "application 'kdu_compress' which shows the potential of the "
                + "JPEG 2000 Developers' Toolkit. The service uses the 'kdu_compress' "
                + "command line tool. This command line application uses a simple "
                + "TIFF file reader which can only read uncompressed TIFF files.  "
                + "This has nothing to do with Kakadu itself. In order to be able to "
                + "read compressed TIFF files, it should be sufficient "
                + "to re-compile the command line application with the symbol "
                + "KDU_INCLUDE_TIFF defined, and link it against the public-domain "
                + "LIBTIFF library. Furthermore, it is possible to compress various"
                + "inputfiles into one jpeg2000 file. But this service only supports"
                + "the one tif input file to one jp2 output file migration. " + "IMPORTANT: "
                + "The license conditions for this web service apply as if you"
                + "were downloading and using the kakadu executables locally. " + "KAKADU LICENSE NOTE: "
                + "Copyright is owned by NewSouth Innovations Proprietary Ltd, "
                + "commercial arm of the University of New South Wales, Sydney, "
                + "Australia. You are free to play around with these executables "
                + "and even to re-distribute them, so long as such use or "
                + "re-distribution is accompanied this copyright notice and is "
                + "not for commercial gain. Note: Binaries can only be used "
                + "for non-commercial purposes. If in doubt please contact " + "Dr. Taubman."
                + "Notice: Using this web service, you are acknowledging and "
                + "accepting these terms and conditions. ");
        FormatRegistry registry = FormatRegistryFactory.getFormatRegistry();

        MigrationPath[] mPaths = new MigrationPath[] {
                new MigrationPath(registry.createExtensionUri("tif"), registry.createExtensionUri("jp2"), null),
                new MigrationPath(registry.createExtensionUri("raw"), registry.createExtensionUri("jp2"), null),
                new MigrationPath(registry.createExtensionUri("bmp"), registry.createExtensionUri("jp2"), null),
                new MigrationPath(registry.createExtensionUri("pbm"), registry.createExtensionUri("jp2"), null),
                new MigrationPath(registry.createExtensionUri("pgm"), registry.createExtensionUri("jp2"), null),
                new MigrationPath(registry.createExtensionUri("ppm"), registry.createExtensionUri("jp2"), null) };
        builder.paths(mPaths);
        builder.classname(this.getClass().getCanonicalName());
        builder.version("0.1");
        builder.parameters(parameters);
        ServiceDescription mds = builder.build();

        return mds;
    }
}