eu.esdihumboldt.hale.common.headless.transform.TransformationWorkspace.java Source code

Java tutorial

Introduction

Here is the source code for eu.esdihumboldt.hale.common.headless.transform.TransformationWorkspace.java

Source

/*
 * Copyright (c) 2012 Data Harmonisation Panel
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Data Harmonisation Panel <http://www.dhpanel.eu>
 */

package eu.esdihumboldt.hale.common.headless.transform;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.joda.time.Duration;
import org.joda.time.ReadableDuration;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

import de.fhg.igd.osgi.util.OsgiUtils;
import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.core.io.IOProvider;
import eu.esdihumboldt.hale.common.core.io.project.model.IOConfiguration;
import eu.esdihumboldt.hale.common.core.io.supplier.FileIOSupplier;
import eu.esdihumboldt.hale.common.core.io.supplier.LocatableOutputSupplier;
import eu.esdihumboldt.hale.common.headless.EnvironmentService;
import eu.esdihumboldt.hale.common.headless.HeadlessIO;
import eu.esdihumboldt.hale.common.headless.TransformationEnvironment;
import eu.esdihumboldt.hale.common.headless.WorkspaceService;
import eu.esdihumboldt.hale.common.headless.report.ReportFile;
import eu.esdihumboldt.hale.common.instance.io.InstanceReader;
import eu.esdihumboldt.hale.common.instance.io.InstanceWriter;

/**
 * A transformation workspace based on {@link WorkspaceService} and
 * {@link EnvironmentService}.
 * 
 * @author Simon Templer
 */
public class TransformationWorkspace {

    private static final ALogger log = ALoggerFactory.getLogger(TransformationWorkspace.class);

    /**
     * Name of the report file placed in a workspace folder.
     */
    private static final String WORKSPACE_REPORT_FILE = "reports.log";

    /**
     * Name of the folder containing the source files in a transformation
     * workspace.
     */
    private static final String WORKSPACE_SOURCE_FOLDER = "source";

    /**
     * Name of the folder containing the target files in a transformation
     * workspace.
     */
    private static final String WORKSPACE_TARGET_FOLDER = "target";

    /**
     * Name of the workspace setting that holds information about the completion
     * of the transformation.
     */
    private static final String SETTING_TRANSFORMATION_SUCCESS = "transformationSuccess";

    private final WorkspaceService workspaces;

    private final String workspaceId;

    private final File workspace;

    private final File targetFolder;

    private final File sourceFolder;

    private final File reportFile;

    /**
     * Create a new transformation workspace with a lease duration of one day.
     * 
     * @throws IllegalStateException if the {@link WorkspaceService} is not
     *             available
     */
    public TransformationWorkspace() {
        this(Duration.standardDays(1));
    }

    /**
     * Create a new transformation workspace with a custom lease duration.
     * 
     * @param leaseDuration the lease duration of the workspace
     * @throws IllegalStateException if the {@link WorkspaceService} is not
     *             available
     */
    public TransformationWorkspace(ReadableDuration leaseDuration) {
        this(null, leaseDuration);
    }

    /**
     * Create a representation of an existing transformation workspace.
     * 
     * @param workspaceId the workspace identifier
     * @throws IllegalStateException if the {@link WorkspaceService} is not
     *             available or the workspace with the given identifier does not
     *             exist
     */
    public TransformationWorkspace(String workspaceId) {
        this(workspaceId, null);
    }

    /**
     * Create a new workspace or use an existing one.
     * 
     * @param workspaceId the workspace identifier if this object should
     *            represent an existing workspace, may be <code>null</code> if
     *            leaseDuration is set.
     * @param leaseDuration the lease duration of a new workspace to create, may
     *            be <code>null</code> if workspaceId is set
     * @throws IllegalStateException if the {@link WorkspaceService} is not
     *             available or the workspace with the given identifier does not
     *             exist
     */
    protected TransformationWorkspace(final String workspaceId, ReadableDuration leaseDuration) {
        workspaces = OsgiUtils.getService(WorkspaceService.class);

        if (workspaces == null) {
            throw new IllegalStateException("WorkspaceService not available through OSGi");
        }

        if (workspaceId == null) {
            this.workspaceId = workspaces.leaseWorkspace(leaseDuration);
        } else {
            this.workspaceId = workspaceId;
        }

        try {
            workspace = workspaces.getWorkspaceFolder(this.workspaceId);
        } catch (FileNotFoundException e) {
            throw new IllegalStateException("Error accessing transformation workspace");
        }

        sourceFolder = new File(workspace, WORKSPACE_SOURCE_FOLDER);
        sourceFolder.mkdir();

        targetFolder = new File(workspace, WORKSPACE_TARGET_FOLDER);
        targetFolder.mkdir();

        // report file
        reportFile = new File(workspace, WORKSPACE_REPORT_FILE);
    }

    /**
     * Transform the instances provided through the given instance readers and
     * store the result in the {@link #getTargetFolder()}.
     * 
     * @param envId the environment ID
     * @param sources the instance readers
     * @param target the configuration of the target instance writer
     * @return the future representing the successful completion of the
     *         transformation (note that a successful completion doesn't
     *         necessary mean there weren't any internal transformation errors)
     * @throws Exception if launching the transformation fails
     */
    public ListenableFuture<Boolean> transform(String envId, List<InstanceReader> sources, IOConfiguration target)
            throws Exception {
        EnvironmentService environments = OsgiUtils.getService(EnvironmentService.class);
        if (environments == null) {
            throw new IllegalStateException("WorkspaceService not available through OSGi");
        }
        TransformationEnvironment env = environments.getEnvironment(envId);

        if (env == null) {
            throw new IllegalStateException("Transformation environment for project " + envId + " not available.");
        }

        return transform(env, sources, target, null);
    }

    /**
     * Transform the instances provided through the given instance readers and
     * by default stores the result in the {@link #getTargetFolder()}.
     * 
     * @param env the transformation environment
     * @param sources the instance readers
     * @param target the configuration of the target instance writer
     * @param customTarget the custom output supplier to use for the target,
     *            <code>null</code> to use the default target in thet
     *            {@link #getTargetFolder()}
     * @return the future representing the successful completion of the
     *         transformation (note that a successful completion doesn't
     *         necessary mean there weren't any internal transformation errors)
     * @throws Exception if launching the transformation fails
     */
    public ListenableFuture<Boolean> transform(TransformationEnvironment env, List<InstanceReader> sources,
            IOConfiguration target, LocatableOutputSupplier<? extends OutputStream> customTarget) throws Exception {
        InstanceWriter writer = (InstanceWriter) HeadlessIO.loadProvider(target);
        // TODO determine content type if not set?

        // output file
        if (customTarget != null) {
            writer.setTarget(customTarget);
        } else {
            File out = new File(targetFolder, "result." + getFileExtension(writer.getContentType()));
            writer.setTarget(new FileIOSupplier(out));
        }

        ListenableFuture<Boolean> result = Transformation.transform(sources, writer, env,
                new ReportFile(reportFile), workspace.getName());

        Futures.addCallback(result, new FutureCallback<Boolean>() {

            @Override
            public void onSuccess(Boolean result) {
                try {
                    setTransformationSuccess(result);
                } catch (IOException e) {
                    log.error("Failed to set transformation success for workspace", e);
                }
            }

            @Override
            public void onFailure(Throwable t) {
                try {
                    setTransformationSuccess(false);
                } catch (IOException e) {
                    log.error("Failed to set transformation success for workspace", e);
                }
            }
        });

        return result;
    }

    /**
     * Determines if a previously with
     * {@link #transform(String, List, IOConfiguration)} started transformation
     * process is finished. Regardless of the success or failure.
     * 
     * @return <code>true</code> if the transformation is finished,
     *         <code>false</code> if the transformation is still running, no
     *         transformation was started or the workspace no longer exists
     */
    public boolean isTransformationFinished() {
        try {
            Map<String, String> settings = workspaces.getSettings(workspaceId);
            return settings.containsKey(SETTING_TRANSFORMATION_SUCCESS);
        } catch (IOException e) {
            // ignore
            return false;
        }
    }

    /**
     * Determines if a previously with
     * {@link #transform(String, List, IOConfiguration)} started transformation
     * process was complete successfully. Note that a successful completion
     * doesn't necessary mean there weren't any internal transformation errors.
     * The {@link #getReportFile()} holds more detailed information.<br>
     * <br>
     * This method may only be called of the transformation is finished,
     * otherwise an {@link IllegalStateException} will be thrown.
     * 
     * @return if the transformation was completed successfully
     * @throws IllegalStateException if the transformation is not finished
     * 
     * @see #isTransformationFinished()
     */
    public boolean isTransformationSuccessful() throws IllegalStateException {
        try {
            Map<String, String> settings = workspaces.getSettings(workspaceId);
            String success = settings.get(SETTING_TRANSFORMATION_SUCCESS);
            if (success == null) {
                throw new IllegalStateException("Transformation not finished");
            }
            return Boolean.parseBoolean(success);
        } catch (IOException e) {
            // ignore
            return false;
        }
    }

    /**
     * Set if the transformation was successfully completed. Must be called when
     * the transformation is finished. Also deletes the source folder in the
     * workspace.
     * 
     * @param success if the transformation was completed successfully
     * @throws FileNotFoundException if the workspace does not exist
     * @throws IOException if the workspace configuration file cannot be read or
     *             written
     */
    protected void setTransformationSuccess(boolean success) throws FileNotFoundException, IOException {
        workspaces.set(workspaceId, SETTING_TRANSFORMATION_SUCCESS, String.valueOf(success));

        FileUtils.deleteDirectory(getSourceFolder());
    }

    /**
     * Get the workspace settings.
     * 
     * @return the current workspace settings, changes to the map will not be
     *         reflected in the settings
     * @throws FileNotFoundException if the workspace does not exist
     * @throws IOException if the workspace configuration file cannot be read
     * 
     * @see #set(String, String)
     */
    public Map<String, String> getSettings() throws FileNotFoundException, IOException {
        return workspaces.getSettings(workspaceId);
    }

    /**
     * Change a workspace setting.
     * 
     * @param setting the name of the setting
     * @param value the value, <code>null</code> to remove the setting
     * @throws FileNotFoundException if the workspace does not exist
     * @throws IOException if the workspace configuration file cannot be read or
     *             written
     * 
     * @see #getSettings()
     */
    public void set(String setting, String value) throws FileNotFoundException, IOException {
        workspaces.set(workspaceId, setting, value);
    }

    /**
     * Guess the file extension for a given I/O configuration.
     * 
     * @param config the I/O provider configuration
     * @return the file extensions or a default, w/o leading dot
     */
    public static String guessFileExtension(IOConfiguration config) {
        String id = config.getProviderConfiguration().get(IOProvider.PARAM_CONTENT_TYPE).as(String.class);
        IContentType contentType = null;
        if (id != null) {
            contentType = Platform.getContentTypeManager().getContentType(id);
        }
        return getFileExtension(contentType);
    }

    /**
     * Get the default file extension for the given content type.
     * 
     * @param contentType the content type, may be <code>null</code>
     * @return the file extension w/o leading dot
     */
    private static String getFileExtension(IContentType contentType) {
        if (contentType != null) {

            String[] extensions = contentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
            if (extensions != null && extensions.length > 0) {
                return extensions[0];
            }
        }

        // fall-back
        return "out";
    }

    /**
     * @return the workspace ID
     */
    public String getId() {
        return workspaceId;
    }

    /**
     * @return the workspace folder
     */
    public File getWorkspace() {
        return workspace;
    }

    /**
     * Get the target folder. This folder holds the transformation results after
     * the transformation is finished and successful.
     * 
     * @return the target folder
     * 
     * @see #isTransformationFinished()
     * @see #isTransformationSuccessful()
     */
    public File getTargetFolder() {
        return targetFolder;
    }

    /**
     * Get the source folder. Files placed in this folder will be deleted after
     * the transformation has finished.
     * 
     * @return the source folder
     */
    public File getSourceFolder() {
        return sourceFolder;
    }

    /**
     * Get the report file. It holds information about the finished
     * transformation.
     * 
     * @return the report file
     * 
     * @see #isTransformationFinished()
     */
    public File getReportFile() {
        return reportFile;
    }

    /**
     * Delete the workspace
     */
    public void delete() {
        workspaces.deleteWorkspace(workspaceId);
    }

}