Package eu.planets_project.ifr.core.services.migration.genericwrapper2

Source Code of eu.planets_project.ifr.core.services.migration.genericwrapper2.GenericMigrationWrapper

package eu.planets_project.ifr.core.services.migration.genericwrapper2;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.w3c.dom.Document;

import eu.planets_project.ifr.core.common.conf.Configuration;
import eu.planets_project.ifr.core.services.migration.genericwrapper2.exceptions.ConfigurationException;
import eu.planets_project.ifr.core.services.migration.genericwrapper2.exceptions.MigrationException;
import eu.planets_project.ifr.core.services.migration.genericwrapper2.exceptions.MigrationInitialisationException;
import eu.planets_project.ifr.core.services.migration.genericwrapper2.utils.ParameterBuilder;
import eu.planets_project.ifr.core.services.migration.genericwrapper2.utils.ParameterReader;
import eu.planets_project.services.datatypes.Agent;
import eu.planets_project.services.datatypes.Content;
import eu.planets_project.services.datatypes.DigitalObject;
import eu.planets_project.services.datatypes.Event;
import eu.planets_project.services.datatypes.Parameter;
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.MigrateResult;
import eu.planets_project.services.utils.DigitalObjectUtils;
import eu.planets_project.services.utils.ProcessRunner;
import eu.planets_project.services.utils.ServicePerformanceHelper;

/**
*
* @author Thomas Skou Hansen <tsh@statsbiblioteket.dk>
*/
public class GenericMigrationWrapper {

    private Logger log = Logger.getLogger(GenericMigrationWrapper.class
      .getName());

    private MigrationPaths migrationPaths;
    private final String toolIdentifier;
    private final TemporaryFileFactory tempFileFactory;
    private final List<Parameter> environmentParameters;

    private ServiceDescription serviceDescription;

    /**
     * @param configuration
     * @param environmentSettings
     * @param toolIdentifier
     * @throws MigrationInitialisationException
     */
    public GenericMigrationWrapper(Document configuration,
      Configuration environmentSettings, String toolIdentifier)
      throws MigrationInitialisationException {

  this.toolIdentifier = toolIdentifier;

  this.tempFileFactory = new J2EETempFileFactory(toolIdentifier);

  this.environmentParameters = ParameterBuilder.buid(environmentSettings);

  try {
      MigrationPathFactory pathsFactory = new DBMigrationPathFactory(
        configuration);
      this.migrationPaths = pathsFactory.getAllMigrationPaths();

      String serviceProvider = "Undefined - please assign the correct "
        + "service provider identifier to the \"serviceprovider\""
        + " property in the property file for this service.";
      for (Parameter environmentParameter : this.environmentParameters) {
    if ("serviceprovider".equals(environmentParameter.getName())) {
        serviceProvider = environmentParameter.getValue();
    }
      }

      final ServiceDescriptionFactory serviceFactory = new ServiceDescriptionFactory(
        toolIdentifier, serviceProvider, configuration);
      this.serviceDescription = serviceFactory.getServiceDescription();
  } catch (Exception e) {
      throw new MigrationInitialisationException(
        "Failed initialising migration path data from the configuration document: "
          + configuration.getNodeName(), e);
  }
    }

    /**
     * Get the ServiceDescription for this migrate service
     *
     * @return the serviceDescription
     */
    public ServiceDescription describe() {
  return this.serviceDescription;
    }

    /**
     * Migrate the <code>digitalObject</code> from <code>inputFormat</code> to
     * <code>outputFormat</code>, applying the parameters provided by
     * <code>toolParameters</code>.
     *
     * FIXME! Return by reference must be specified by the parameter:
     * returnbyreference=true/false. Default is true.
     *
     * @param digitalObject
     *            the digital object to migrate
     * @param inputFormat
     *            the format of the digital object
     * @param outputFormat
     *            The
     * @param toolParameters
     *            the parameters. If null, is initialised as an empty list.
     * @return the migrateResult for the migration
     * @throws MigrationException
     *             if the generic wrapper failed in invoking the tool and
     *             migrating the digital object. Tool failures are embedded in
     *             the migrateResult.
     *
     *             FIXME! Consider not throwing exceptions as that will only
     *             force wrapping developers to add cut-and-paste exception
     *             handling on the outside. Thus, it should rather be put inside
     *             this method.
     *@throws ConfigurationException
     */
    @SuppressWarnings("boxing")
  public MigrateResult migrate(DigitalObject digitalObject, URI inputFormat,
      URI outputFormat, List<Parameter> toolParameters)
      throws MigrationException, ConfigurationException {

  final ServicePerformanceHelper servicePerformanceHelper = new ServicePerformanceHelper();
  final Date migrationStartTime = new Date();

  /*
   * Validate that the proper parameters are set for the migration path
   * identified by inputFormat and outputFormat
   */
  final MigrationPath migrationPath = this.migrationPaths.getMigrationPath(
    inputFormat, outputFormat);

  // If called with null parameters, use an empty list instead
  if (toolParameters == null) {
      this.log.warning("Called with null parameters. Assuming the caller ment"
        + " to call with an empty list.");
      toolParameters = new ArrayList<Parameter>();
  }

  // Prepare any necessary temporary files.
  final Map<String, File> temporaryFileMappings = createTemporaryFiles(migrationPath);

  // Prepare the data to migrate
  InputStream standardInputStream = null;
  final ToolIOProfile inputIOProfile = migrationPath
    .getToolInputProfile();
  if (inputIOProfile.usePipedIO()) {

      // Serve the digital object through standard input
      standardInputStream = digitalObject.getContent().getInputStream();
  } else {

      // Serve the digital object through a temporary input file.
      File inputTempFile = temporaryFileMappings.get(inputIOProfile
        .getCommandLineFileLabel());
      DigitalObjectUtils.toFile(digitalObject, inputTempFile);
  }

  // Create an executable command line for the process runner.
  final PRCommandBuilder commandBuilder = new PRCommandBuilder(
    this.environmentParameters);
  final List<String> prCommand = commandBuilder.buildCommand(
    migrationPath, toolParameters, temporaryFileMappings);

  if (this.log.isLoggable(Level.INFO)) {
      String fullCommandLine = "";
      for (String cmdfrag : prCommand) {
    fullCommandLine += cmdfrag + " ";
      }
      this.log.info("Executing command line: " + fullCommandLine);
  }

  // Execute the tool
  final ProcessRunner toolProcessRunner = new ProcessRunner();
  final boolean executionSuccessful = executeToolProcess(
    toolProcessRunner, prCommand, standardInputStream);

  // Delete temporary files. However, do NOT delete the output unless the
  // execution failed.

  final ToolIOProfile outputIOProfile = migrationPath
    .getToolOutputProfile();

  if ((outputIOProfile.usePipedIO() == false) && executionSuccessful) {
      // OK, there should exist an output file. Avoid deleting it.
      final String outputFileLabel = outputIOProfile
        .getCommandLineFileLabel();
      for (String tempFileLabel : temporaryFileMappings.keySet()) {
    if (outputFileLabel.equals(tempFileLabel) == false) {
        temporaryFileMappings.get(tempFileLabel).delete();
    }
      }
  } else {
      // The output has been returned through a pipe, so it is safe to
      // delete all files.
      for (File tempFile : temporaryFileMappings.values()) {
    tempFile.delete();
      }
  }

  if (executionSuccessful == false) {
      return buildMigrationResult(migrationPath, digitalObject, null,
        toolProcessRunner);
  }

  // Now create a digital object from the tools output.
  DigitalObject.Builder builder;

  final ParameterReader parameterReader = new ParameterReader(
    toolParameters);
  final boolean returnDataByReference = parameterReader
    .getBooleanParameter("returnByReference", true);

  final ToolIOProfile toolOutputProfile = migrationPath
    .getToolOutputProfile();
  if (toolOutputProfile.usePipedIO() == false) {

      // The tool has written the output to a temporary file. Create a
      // digital object based on that.
      final File outputFile = temporaryFileMappings.get(toolOutputProfile
        .getCommandLineFileLabel());
      if (returnDataByReference) {
    builder = new DigitalObject.Builder(Content
      .byReference(outputFile));
    // We cannot tell when the temporary file can be deleted, so let
    // it live.
      } else {
    builder = new DigitalObject.Builder(Content.byValue(outputFile));

    // It is now safe to delete the temporary file.
    outputFile.delete();
      }
  } else {

      // The tool has written the output to standard output. Create a
      // digital object based on that output.
      if (returnDataByReference) {
    // Direct the standard output contents to a temporary file.
    builder = new DigitalObject.Builder(Content
      .byReference(toolProcessRunner.getProcessOutput()));
      } else {
    // Return the standard output contents by value.
    builder = new DigitalObject.Builder(Content
      .byValue(toolProcessRunner.getProcessOutput()));
      }
  }

  final double migrationDuration = new Date().getTime()
    - migrationStartTime.getTime();

  builder.format(outputFormat);
  final Agent agent = new Agent(this.toolIdentifier, this.serviceDescription
    .getName(), this.serviceDescription.getType());

  String eventSummary = "Migration carried out by executing the command line:";
  for (String commandLineFragment : prCommand) {
      eventSummary += " " + commandLineFragment;
  }
  eventSummary += "\n\nThe migration service was called with these parameters:\n\n";
  for (Parameter serviceParameter : toolParameters) {
      eventSummary += serviceParameter.getName() + " = "
        + serviceParameter.getValue() + "\n";
  }

  servicePerformanceHelper.stop();

  // Add information about the migration event to the digital object.
  final DateFormat defaultDateFormat = DateFormat.getDateInstance();
  final Event event = new Event(eventSummary, defaultDateFormat
    .format(migrationStartTime), migrationDuration, agent,
    servicePerformanceHelper.getPerformanceProperties());
  builder.events(event);

  final DigitalObject resultObject = builder.build();

  return buildMigrationResult(migrationPath, digitalObject, resultObject,
    toolProcessRunner);
    }

    private MigrateResult buildMigrationResult(MigrationPath migrationPath,
      DigitalObject inputObject, DigitalObject resultObject,
      ProcessRunner toolProcessRunner) {

  String stdoutPart = String.format("Standard output:\n%s\n\n",
    toolProcessRunner.getProcessOutputAsString());

  // Default to a service report for a failed execution.
  String statusDescription = "Failed migrating";
  Type messageType = Type.ERROR;
  Status messageStatus = Status.TOOL_ERROR;

  if (toolProcessRunner.getReturnCode() == 0) {
      // Create a service report for a successful execution.

      statusDescription = "Successfully migrated";
      messageType = Type.INFO;
      messageStatus = Status.SUCCESS;

      final ToolIOProfile toolOutputProfile = migrationPath
        .getToolOutputProfile();

      if (toolOutputProfile.usePipedIO()) {
    // Avoid printing standard output if the digital object was
    // returned through that.
    stdoutPart = "";
      }
  }

  // Fill in the blanks in the generic message template.
  final String reportMessage = String.format("%s object with title '%s'"
    + " from format URI: '%s' to '%s'.\n%sStandard error output:"
    + "\n%s", statusDescription, inputObject.getTitle(),
    migrationPath.getInputFormat(),
    migrationPath.getOutputFormat(), stdoutPart, toolProcessRunner
      .getProcessErrorAsString());

  final ServiceReport serviceReport = new ServiceReport(messageType,
    messageStatus, reportMessage);

  return new MigrateResult(resultObject, serviceReport);
    }

    /**
     * Create all the temporary files needed in order to perform a migration
     * using the migration path specified by <code>migrationPath</code> and
     * return a map associating the command labels with the appropriate file
     * path.
     *
     * @param migrationPath
     *            The migration path to create temporary files for.
     * @return A <code>Map</code> associating the labels in the command line
     *         which identifies temporary files with the actual file paths for
     *         the files.
     */
    private Map<String, File> createTemporaryFiles(MigrationPath migrationPath)
      throws MigrationException {

  Map<String, File> temporaryFileMappings = new HashMap<String, File>();

  final ToolIOProfile toolInputProfile = migrationPath
    .getToolInputProfile();

  try {
      if (!toolInputProfile.usePipedIO()) {

    temporaryFileMappings = createTemporaryFile(
      temporaryFileMappings, toolInputProfile
        .getCommandLineFileLabel(), toolInputProfile
        .getDesiredTempFileName());

    final String tempFileLabel = toolInputProfile
      .getCommandLineFileLabel();
    this.log.info(String.format("Created a temporary input file. "
      + "Label = '%s'. " + "File name: '%s'", tempFileLabel,
      temporaryFileMappings.get(tempFileLabel)
        .getCanonicalPath()));
      }

      final ToolIOProfile toolOutputProfile = migrationPath
        .getToolOutputProfile();
      if (!toolOutputProfile.usePipedIO()) {

    temporaryFileMappings = createTemporaryFile(
      temporaryFileMappings, toolOutputProfile
        .getCommandLineFileLabel(), toolOutputProfile
        .getDesiredTempFileName());

    final String tempFileLabel = toolOutputProfile
      .getCommandLineFileLabel();
    this.log.info(String.format("Created a temporary output file. "
      + "Label = '%s'. " + "File name: '%s'", tempFileLabel,
      temporaryFileMappings.get(tempFileLabel)
        .getCanonicalPath()));
      }

      final Map<String, String> temporaryFileDeclarations = migrationPath
        .getTempFileDeclarations();
      for (String tempFileLabel : temporaryFileDeclarations.keySet()) {
    final String desiredFileName = temporaryFileDeclarations
      .get(tempFileLabel);

    temporaryFileMappings = createTemporaryFile(
      temporaryFileMappings, tempFileLabel, desiredFileName);
      }
  } catch (IOException ioe) {
      throw new MigrationException("Failed creating temporary files.",
        ioe);
  }
  return temporaryFileMappings;
    }

    /**
     * Create a temporary file with a random name or with a desired name, if a
     * such is specified. If the caller has no desired file name then
     * <code>desiredFileName</code> must be <code>null</code>.
     * <p/>
     * The created file will be added to <code>tempFileMap</code>, using
     * <code>fileLabel</code> as the key.
     * <p/>
     * Files that are given a random name will have <code>fileLabel</code>
     * appended to the file name to make debugging easier.
     *
     * @param tempFileMap
     *            A file map to add the created temporary file to.
     * @param fileLabel
     *            The label/key which the generated file must be associated with
     *            in the returned map.
     * @param desiredFileName
     *            The desired name of the temporary file or <code>null</code> if
     *            the file should be given a random name.
     * @return <code>tempFileMap</code> with the created file added.
     */
    private Map<String, File> createTemporaryFile(
      Map<String, File> tempFileMap, String fileLabel,
      String desiredFileName) {
  File temporaryFile = null;

  if (desiredFileName == null) {
      // No desired name has been specified. Create a file with a random
      // name having the file label added to give a clue in case of
      // debugging becomes necessary.
      temporaryFile = this.tempFileFactory
        .prepareRandomNamedTempFile(fileLabel);
  } else {
      // Create a temporary file with the desired base name.
      temporaryFile = this.tempFileFactory.prepareTempFile(desiredFileName);
  }

  tempFileMap.put(fileLabel, temporaryFile);
  return tempFileMap;
    }

    /**
     * Execute the command line, described by the list of strings provided by
     * <code>command</code>, using the <code>ProcessRunner</code> provided by
     * <code>toolProcessRunner</code>. The process runner will pass any
     * information from <code>processStandardInput</code> on to the command
     * through the standard input.
     *
     * @param toolProcessRunner
     *            <code>ProcessRunner</code> instance for execution of the
     *            command line.
     * @param command
     *            A list of strings constituting a command line to execute.
     * @param processStandardInput
     *            An input stream for passing information to be piped to the
     *            command through standard input.
     * @return <code>true</code> if the execution was successful and otherwise
     *         <code>false</code>.
     */
    private boolean executeToolProcess(ProcessRunner toolProcessRunner,
      List<String> command, InputStream processStandardInput) {

  toolProcessRunner.setInputStream(processStandardInput);
  toolProcessRunner.setCommand(command);
  toolProcessRunner.setCollection(true);
  toolProcessRunner.setOutputCollectionByteSize(-1);

  toolProcessRunner.run();
  return toolProcessRunner.getReturnCode() == 0;
    }
}
TOP

Related Classes of eu.planets_project.ifr.core.services.migration.genericwrapper2.GenericMigrationWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.