Package eu.scape_project.planning.plato.wf

Source Code of eu.scape_project.planning.plato.wf.AbstractWorkflowStep

/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package eu.scape_project.planning.plato.wf;

import java.io.File;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.slf4j.Logger;

import eu.scape_project.planning.bean.PrepareChangesForPersist;
import eu.scape_project.planning.exception.PlanningException;
import eu.scape_project.planning.manager.ByteStreamManager;
import eu.scape_project.planning.manager.DigitalObjectManager;
import eu.scape_project.planning.manager.PlanManager;
import eu.scape_project.planning.manager.StorageException;
import eu.scape_project.planning.model.Alternative;
import eu.scape_project.planning.model.DigitalObject;
import eu.scape_project.planning.model.Plan;
import eu.scape_project.planning.model.PlanState;
import eu.scape_project.planning.model.SampleObject;
import eu.scape_project.planning.model.User;
import eu.scape_project.planning.services.characterisation.fits.FitsIntegration;
import eu.scape_project.planning.utils.XmlXPathEvaluator;
import eu.scape_project.planning.validation.PlanValidator;
import eu.scape_project.planning.validation.ValidationError;

/**
* Base class for steps of the workflow.
*
* @author Markus Hamm, Michael Kraxner
*/
public abstract class AbstractWorkflowStep implements Serializable {

    private static final long serialVersionUID = -517256120799033395L;

    @Inject
    private Logger log;

    @Inject
    protected ByteStreamManager bytestreamManager;

    @Inject
    protected DigitalObjectManager digitalObjectManager;

    @Inject
    protected EntityManager em;

    @Inject
    protected PlanManager planManager;

    @Inject
    protected PlanValidator planValidator;

    @Inject
    protected User user;

    @Inject
    protected PrepareChangesForPersist prepareChangesForPersist;

    @Inject
    private FitsIntegration fits;

    @Inject
    private XmlXPathEvaluator xmlXPathEvaluator;

    protected Plan plan;

    /**
     * Set containing a list of bytestreams(pids) stored since the last
     * action-execution (save, discard).
     */
    protected Set<String> addedBytestreams = new HashSet<String>();

    /**
     * Set containing a list of bytestreams(pids) marked to delete since the
     * last action-execution (save, discard).
     */
    protected Set<String> bytestreamsToRemove = new HashSet<String>();

    protected PlanState requiredPlanState;
    protected PlanState correspondingPlanState;

    /**
     * Empty constructor.
     */
    public AbstractWorkflowStep() {
    }

    /**
     * Initializes this workflow step.
     *
     * @param p
     *            the plan
     */
    public void init(Plan p) {
        plan = p;
        prepareChangesForPersist.setUser(user.getUsername());
    }

    /**
     * Saves the plan. Updates the plan state if the current step is complete.
     * Otherwise the plan state is reset to the current state and errors are
     * added to the error list.
     *
     * @param errors
     *            a list of errors
     * @return true if there were no errors, false otherwise
     */
    public boolean proceed(List<ValidationError> errors) {
        // save the plan - this way the state is reset to the requiredPlanState,
        // and changes are not lost
        save();

        if (mayProceed(errors)) {
            plan.getPlanProperties().setState(correspondingPlanState);
            saveEntity(plan.getPlanProperties());
            return errors.isEmpty();
        }
        return false;
    }

    /**
     * Checks if the plan may proceed to the next step.
     *
     * @param errors
     *            a list of errors
     * @return true if the plan may proceed, false otherwise
     */
    protected boolean mayProceed(List<ValidationError> errors) {
        return planValidator.isPlanStateSatisfied(plan, correspondingPlanState, errors);
    }

    /**
     * Stores all changes made in a step.
     *
     * Resets the plan's state to requiredPlanState and persist it. Saves
     * step-specific changes and cleans up added or deleted bytestreams.
     *
     * Note: derived steps have to store their changes in
     * {@link AbstractWorkflowStep#saveStepSpecific()}
     */
    public void save() {
        plan.getPlanProperties().setState(requiredPlanState);
        plan.getPlanProperties().touch();

        saveWithoutModifyingPlanState();
    }

    /**
     * Stores all changes made in a step WITHOUT modifying the plan-state. This
     * method is often required at re-using the business-logic of a
     * workflow-step.
     *
     * Saves plan properties, step-specific changes and cleans-up added/deleted
     * bytestreams.
     */
    public void saveWithoutModifyingPlanState() {
        saveEntity(plan.getPlanProperties());

        saveStepSpecific();

        // Added bytestreams are accepted
        addedBytestreams.clear();

        // Delete bytestreams marked to remove
        for (String pid : bytestreamsToRemove) {
            try {
                bytestreamManager.delete(pid);
            } catch (StorageException e) {
                log.error("failed to delete bytestream: " + pid);
            }
        }
        bytestreamsToRemove.clear();
    }

    /**
     * Persists the provided entity.
     *
     * @param entity
     *            Entity to persist
     * @return The entity merged into current persistence context.
     */
    protected Object saveEntity(Object entity) {
        prepareChangesForPersist.prepare(entity);

        Object merged = em.merge(entity);
        em.persist(merged);
        return merged;
    }

    /**
     * Removes the provided entity from the database.
     *
     * @param entity
     *            Entity to remove
     */
    protected void removeEntity(Object entity) {
        Object merged = em.merge(entity);
        em.remove(merged);
    }

    /**
     * Discards all changes that have not been persisted so far.
     *
     * @throws PlanningException
     *             if the reload failed
     */
    public void discard() throws PlanningException {
        // Delete added bytestreams
        for (String pid : addedBytestreams) {
            try {
                bytestreamManager.delete(pid);
            } catch (StorageException e) {
                log.error("Failed to delete discarded bytestream: " + pid);
            }
        }
        addedBytestreams.clear();

        // Clear discarded bytestreams
        bytestreamsToRemove.clear();

        plan = planManager.reloadPlan(plan);
    }

    /**
     * Stores all changes made in this step. Define here what you want to save
     * in derived steps.
     */
    protected abstract void saveStepSpecific();

    /**
     * Method responsible for retrieving a copy of a previously uploaded result
     * file.
     *
     * @param alternative
     *            Alternative the file was uploaded for
     * @param sampleObject
     *            Sample the file was uploaded for
     * @return A copy of the result file as DigitalObject
     * @throws StorageException
     *             if any error occurred retrieving the result file
     */
    public DigitalObject fetchResultFile(Alternative alternative, SampleObject sampleObject) throws StorageException {
        DigitalObject digitalObject = alternative.getExperiment().getResults().get(sampleObject);

        return digitalObjectManager.getCopyOfDataFilledDigitalObject(digitalObject);
    }

    /**
     * Fetches a copy of the provided digital object filled with data.
     *
     * @param object
     *            the object to fetch
     * @return a data filled digital object
     * @throws StorageException
     *             if any error occurred fetching data
     */
    public DigitalObject fetchDigitalObject(DigitalObject object) throws StorageException {
        return digitalObjectManager.getCopyOfDataFilledDigitalObject(object);
    }

    /**
     * Characterizes a digital object with FITS tool.
     *
     * @param digitalObject
     *            digital object to characterize
     * @param updateFormatInfo
     *            true to update the format info, false otherwise
     * @return true if characterization was successful, false otherwise
     */
    public boolean characteriseFits(DigitalObject digitalObject, boolean updateFormatInfo) {
        if (fits == null) {
            log.debug("FITS is not available and needs to be reconfigured.");
            return false;
        }

        if (digitalObject != null && digitalObject.isDataExistent()) {
            try {
                String fitsXML = null;
                File sampleFile = bytestreamManager.getTempFile(digitalObject.getPid());
                fitsXML = fits.characterise(sampleFile);
                digitalObject.setFitsXMLString(fitsXML);
                log.debug("FITS xml stored in digital-object " + digitalObject.getFullname());

                if (updateFormatInfo) {
                    return updateFormatInformationBasedOnFits(digitalObject);
                }
                return true;
            } catch (PlanningException e) {
                log.error("characterisation with FITS failed.", e);
                return false;
            }
        }

        return false;
    }

    /**
     * Characterizes a digital object with FITS tool and updates the format
     * info.
     *
     * @param digitalObject
     *            digital object to characterize
     * @return true if characterization was successful, false otherwise
     */
    public boolean characteriseFits(DigitalObject digitalObject) {
        return characteriseFits(digitalObject, true);
    }

    /**
     * Updates the format info of the digital object based on the FITS string.
     *
     * @param digitalObject
     *            digital object to update
     * @return true if the format information was updated, false otherwise
     */
    private boolean updateFormatInformationBasedOnFits(DigitalObject digitalObject) {
        if (digitalObject == null || digitalObject.getFitsXMLString() == null
            || digitalObject.getFitsXMLString().isEmpty()) {
            log.debug("Invalid sample object passed to process - stop update format-information.");
            return false;
        }

        try {
            xmlXPathEvaluator.setXmlToParse(digitalObject.getFitsXMLString());

            digitalObject.getFormatInfo().setPuid(
                xmlXPathEvaluator.extractValue("/fits/identification/identity/externalIdentifier[@type='puid']"));
            digitalObject.getFormatInfo().setName(
                xmlXPathEvaluator.extractValue("/fits/identification/identity/attribute::format"));
            digitalObject.getFormatInfo().setVersion(
                xmlXPathEvaluator.extractValue("/fits/identification/identity/version[@toolname='Jhove']"));
            digitalObject.getFormatInfo().setMimeType(
                xmlXPathEvaluator.extractValue("/fits/identification/identity/attribute::mimetype"));
            digitalObject.getFormatInfo().setDefaultExtension(extractFileExtension(digitalObject.getFullname()));

            digitalObject.getFormatInfo().touch();

            log.debug("Successfully updated fomat-information based on FITS for digital object "
                + digitalObject.getFullname());
            return true;
        } catch (Exception e) {
            log.error(
                "Error at updating fomat-information based on FITS for digital object " + digitalObject.getFullname(),
                e);
            return false;
        }
    }

    /**
     * Extracts the file extension of a given filename.
     *
     * @param fileName
     *            filename to parse
     * @return file extension or an empty string if the filename has no
     *         extension
     */
    private String extractFileExtension(String fileName) {
        int separatorIndex = fileName.lastIndexOf('.');

        // if no extension is present in the filename
        if (separatorIndex == -1 || separatorIndex == (fileName.length() - 1)) {
            return "";
        }

        return fileName.substring(separatorIndex + 1);
    }

    // ********** getter/setter **********
    public void setPlan(Plan p) {
        this.plan = p;
    }

    public Plan getPlan() {
        return this.plan;
    }
}
TOP

Related Classes of eu.scape_project.planning.plato.wf.AbstractWorkflowStep

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.