de.zib.gndms.logic.model.TaskAction.java Source code

Java tutorial

Introduction

Here is the source code for de.zib.gndms.logic.model.TaskAction.java

Source

package de.zib.gndms.logic.model;

/*
 * Copyright 2008-2011 Zuse Institute Berlin (ZIB)
 *
 * 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.
 */

import com.google.inject.Inject;
import de.zib.gndms.kit.access.RequiresCredentialProvider;
import de.zib.gndms.kit.access.CredentialProvider;
import de.zib.gndms.kit.configlet.ConfigletProvider;
import de.zib.gndms.kit.util.WidAux;
import de.zib.gndms.logic.action.LogAction;
import de.zib.gndms.logic.model.gorfx.permissions.PermissionConfiglet;
import de.zib.gndms.model.common.types.FilePermissions;
import de.zib.gndms.model.gorfx.AbstractTask;
import de.zib.gndms.model.gorfx.types.AbstractORQ;
import de.zib.gndms.model.gorfx.types.TaskState;
import de.zib.gndms.model.util.EntityManagerAux;
import de.zib.gndms.model.util.TxFrame;
import de.zib.gndms.stuff.copy.Copier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import java.io.Serializable;
import java.util.GregorianCalendar;

/**
 * A TaskAction is used to execute a model of type {@link AbstractTask}s.
 *
 * The method {@link #execute(EntityManager)} invokes {@code transit()} in a loop, wich changes the TaskState of the model and
 * invokes a method corresponding to the TaskSate. A concrete subclass must define the model's action
 * (on {@code IN_PROGRESS}) by implementing {@link #onInProgress(AbstractTask)}. 
 *
 *
 * @author  try ste fan pla nti kow zib
 * @version $Id$
 *
 *          User: stepn Date: 15.09.2008 Time: 11:26:48
 */
@SuppressWarnings({ "AbstractMethodCallInConstructor" })
public abstract class TaskAction extends AbstractModelAction<AbstractTask, AbstractTask>
        implements LogAction, RequiresCredentialProvider {
    /**
     * the ExecutionService on which this TaskAction runs
     */
    private TaskExecutionService service;
    protected Log log = LogFactory.getLog(this.getClass());
    private String wid;
    private Class<? extends AbstractTask> taskClass;
    /**
     * A backup of the model
     */
    private AbstractTask backup;
    /**
     * an EntityManagerFactory in the case that the used EntityManager is broken
     */
    private EntityManagerFactory emf;
    private ConfigletProvider configletProvider;
    private CredentialProvider credentialProvider;
    private boolean detached = false; ///< A detached taskAction only wraps a task which is executed within another taskAction.
                                      /// It doesn't update the task itself only represents the latest state of the task.
    private volatile boolean cancelled = false; ///< set to true if the action should be destroyed from the outside.
    /// If the action enters transit is stops.

    public boolean isDetached() {
        return detached;
    }

    public void setDetached(boolean detached) {
        this.detached = detached;
    }

    public synchronized boolean getCancelled() {
        return cancelled;
    }

    public synchronized void setCancelled(boolean cancel) {
        cancelled = cancel;
    }

    /**
     *
     */
    private static final class ShutdownTaskActionException extends RuntimeException {
        private static final long serialVersionUID = 2772466358157719820L;

        ShutdownTaskActionException(final Exception eParam) {
            super(eParam);
        }
    }

    /**
     * A TransitException is used to store a new desired {@code TaskState}, the model (an AbstractTask) shall use.
     * Its state can be changed using {@link AbstractTask#transit(TaskState)}.
     * Note that there is a predefined order in which state a TaskState can be changed.
     *
     * There a some special subclasses used to stop an AbstractTask in special situations.
     *
     * To stop an AbstractTask use the subclass {@link StopException}.
     * If the model has finished its computation, throw a {@link FinishedException}
     * If something went wrong, throw a {@link FailedException}
     *
     * @see TaskState
     * @see AbstractTask
     */
    protected static class TransitException extends RuntimeException {
        private static final long serialVersionUID = 1101501745642141770L;

        /**
         * the new, desired TaskState for the model.
         * Its state is changed using {@link AbstractTask#transit(TaskState)} 
         */
        protected final TaskState newState;

        /**
         * Creates a new TransitException and stores the new state, a corresponding AbstractTask shall use.
         *
         * @param newStateParam the new TaskState for an AbstractTask
         */
        protected TransitException(final @NotNull TaskState newStateParam) {
            super();
            newState = newStateParam;
        }

        /**
         * Creates a new TransitException and stores the new state, a corresponding AbstractTask shall use.
         * A RuntimeException must be denoted, which will be included in this RuntimeException.
         *
         * @param newStateParam the new TaskState for an AbstractTask
         * @param cause the RuntimeException, which will be included in this RuntimeException
         */
        protected TransitException(final @NotNull TaskState newStateParam, final @NotNull RuntimeException cause) {
            super(cause);
            newState = newStateParam;
        }

        public boolean isDemandingAbort() {
            return false;
        }
    }

    /**
     * A TransitException used to signalize that a failure occured during the model's computation.
     *
     * It sets {@link TransitException#newState} to {@code FAILED} and stores a RuntimeException.
     *
     */
    protected static class FailedException extends TransitException {
        private static final long serialVersionUID = -4220356706557491625L;

        protected FailedException(final @NotNull RuntimeException cause) {
            super(TaskState.FAILED, cause);
        }

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

    /**
      * A TransitException used to signalize that model's computation is finished.
      *
      * It stores the result of the computation and sets {@link TransitException#newState} to {@code FINISHED}
      */
    protected static class FinishedException extends TransitException {
        private static final long serialVersionUID = 196914329532915066L;

        protected final Serializable result;

        protected FinishedException(final Serializable resultParam) {
            super(TaskState.FINISHED);
            result = resultParam;
        }
    }

    /**
      * A TransitException used to signalize that the model's computation has to stop now.
     *
      */
    private static class StopException extends TransitException {
        private static final long serialVersionUID = 7783981039310846994L;

        /**
         * Signalizes that the TaskAction has to stop now.
         * 
         * @param newStateParam the current TaskState of the model.
         */
        protected StopException(final @NotNull TaskState newStateParam) {
            super(newStateParam);
        }

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

    public TaskAction() {
        super();
    }

    public TaskAction(final @NotNull EntityManager em, final @NotNull AbstractTask model) {
        super();
        initFromModel(em, model);
    }

    /**
     * Initializes a TaskAction by denoting an EntityManager and a model.
     *
     * The model is made persistent by the EntityManager.
     * The EntityManager and the model are stored, using {@code setOwnEntityManager()} and {@code setModelAndBackup()}.
     * A Backup of the model is done.
     *
     *
     * @param em an EntityManager, storing AbstractTasks
     * @param model an AbstractTask to be stored as model of {@code this} and to be stored in the database
     */
    public void initFromModel(final EntityManager em, AbstractTask model) {

        boolean wasActive = em.getTransaction().isActive();
        if (!wasActive)
            em.getTransaction().begin();
        try {
            final boolean contained = em.contains(model);
            if (!contained) {
                try {
                    em.persist(model);
                } catch (EntityExistsException e) {
                    model = em.merge(model);
                }
            }
            if (!wasActive)
                em.getTransaction().commit();
            setOwnEntityManager(em);
            setModelAndBackup(model);
        } finally {
            if (em.getTransaction().isActive())
                em.getTransaction().rollback();
        }
    }

    public TaskAction(final @NotNull EntityManager em, final @NotNull String pk,
            Class<? extends AbstractTask> cls) {
        super();
        setTaskClass(cls);
        initFromPk(em, pk);
    }

    /**
     * Initializes a TaskAction by denoting an EntityManager and primary key
     *
     * Retrieves the model (an AbstractTask), managed by the EntityManager, sets it as the model of {@code this} and
     * makes a backup of it.
     *
     * The AbstractTask is select by the primary key {@code pk}. The EntityManager {@code em}
     * will be set as EntityManager for {@code this}, using {@link #setOwnEntityManager(javax.persistence.EntityManager)}.
     *
     * @param em an EntityManager, storing AbstractTasks
     * @param pk the primary key a of specific AbstractTask, which is managed by the EntityManager
     */
    public void initFromPk(final EntityManager em, final String pk) {
        boolean wasActive = em.getTransaction().isActive();
        if (!wasActive)
            em.getTransaction().begin();
        try {
            final AbstractTask model = em.find(getTaskClass(), pk);
            if (model == null)
                throw new IllegalArgumentException("Model not found for pk: " + pk);
            if (!wasActive)
                em.getTransaction().commit();
            setOwnEntityManager(em);
            setModelAndBackup(model);
        } finally {
            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }
        }
    }

    @Override
    public void setModel(final @NotNull AbstractTask mdl) {
        super.setModel(mdl); // Overridden method
        wid = mdl.getWid();
        taskClass = mdl.getClass();
    }

    public void cleanUpOnFail(final @NotNull AbstractTask model) {
        // some actions don't need this
    }

    /**
     * Stores the model {@code mdl} and makes a backup of it using a deep copy, not a reference copy.
     *
     * @see #setModel(de.zib.gndms.model.gorfx.AbstractTask) 
     * @see #setBackup(de.zib.gndms.model.gorfx.AbstractTask) 
     * @param mdl a model to be set
     */
    protected void setModelAndBackup(final @NotNull AbstractTask mdl) {
        setModel(mdl);
        setBackup(Copier.copy(false, mdl));
    }

    protected @NotNull Class<? extends AbstractTask> getTaskClass() {
        return taskClass;
    }

    protected void setTaskClass(Class<? extends AbstractTask> cls) {
        taskClass = cls;
    }

    /**
     * Returns the TaskExecutionService corresponding to this TaskAction or a parent TaskAction.
     *
     * @return the TaskExecutionService corresponding to this TaskAction or a parent TaskAction.
     */
    public TaskExecutionService getService() {
        if (service == null) {
            final TaskAction taskAction = nextParentOfType(TaskAction.class);
            return taskAction == null ? null : taskAction.getService();
        }
        return service;
    }

    public void setService(final @NotNull TaskExecutionService serviceParam) {
        if (service == null)
            service = serviceParam;
        else
            throw new IllegalStateException("Can't overwrite service");
    }

    @Override
    protected final boolean isExecutingInsideTransaction() {
        return false;
    }

    @Override
    public AbstractTask call() throws RuntimeException {
        try {
            final AbstractTask task = getModel();
            if (task != null) {
                WidAux.initWid(getModel().getWid());
                if (task.getOrq() != null) {
                    WidAux.initGORFXid(((AbstractORQ) task.getOrq()).getActId());
                }
            }
            return super.call(); // Overridden method
        } finally {
            WidAux.removeGORFXid();
            WidAux.removeWid();
        }
    }

    /**
     * Invokes {@link #transit(TaskState)} in a loop, which may change the model's TaskState and invokes then a method,
     * corresponding to current {@code TaskState}.
     *
     * It stops as soon as {@code transit} throws a {@code StopException},
     * which is thrown if the model's TaskState is set to {@code FAILED} or {@code FINISHED}.
     * The model is then returned.
     *
     * An implementing class must at least define, what the model is supposed to do, when its TaskState is set to
     * {@code IN_PROGRESS}, using the method {@link #onInProgress(AbstractTask)}.
     * The methods for the other TaskState-values are already implemented (see {@link #onInitialized(AbstractTask)} for example),
     * but may be overwritten by a subclass.
     *
     * Note: Flow control in the loop is done using Exceptions
     *
     * @param em the EntityManager being executed on its persistence context.
     * @return the model after it has been executed
     */
    @SuppressWarnings({ "ThrowableInstanceNeverThrown", "ObjectAllocationInLoop" })
    @Override
    public AbstractTask execute(final @NotNull EntityManager em) {

        boolean first = true;
        TransitException curEx = null;
        do {
            // for debugging
            final String id = getModel().getId();
            final TaskState curState = getModel().getState();
            try {
                try {
                    if (first) {
                        try {
                            final AbstractORQ orq = AbstractORQ.class.cast(getModel().getOrq());
                            trace("execute() with " + orq.getLoggableDescription(), null);
                            trace("transit(null)", null);
                            transit(null);
                        } finally {
                            first = false;
                        }
                    } else {
                        trace("transit(" + curEx.newState + ')', curEx.getCause());
                        transit(curEx.newState);
                    }
                    // if we come here, transit has failed
                    throw new IllegalStateException("No proper TransitException thrown");
                }
                /* On stop: finish loop and return model to caller */
                catch (final StopException newEx) {
                    trace("markAsDone()", null);
                    markAsDone();
                    curEx = null;
                }
                /* On transit: switch to next state according to newEx */
                catch (final TransitException newEx) {
                    curEx = newEx;
                }
                /* On shutdown: Do not set the task to failed, simply throw e */
                catch (final ShutdownTaskActionException e) {
                    throw e;
                }
                /* On runtime ex: Set task to failed */
                catch (RuntimeException e_in) {
                    // no joke
                    final @NotNull RuntimeException e_out = e_in == null ? new NullPointerException() : e_in;
                    trace("catch(RuntimeException)", e_out);
                    /* Cant go to FAILED after FINISHED, i.e. onFinish must never fail */
                    if (TaskState.FINISHED.equals(getModel().getState()))
                        throw e_out; // todo log this one
                    else
                        fail(e_out);
                }
            }
            /* If fail was called due to a RuntimeException in above try block, use resulting
               exception for the next state transition.  Ensures that the failed state of the
               task will be persisted.
             */
            catch (TransitException newEx) {
                curEx = newEx;
            }
        } while (curEx != null);
        trace("return getModel()", null);
        return getModel();
    }

    @SuppressWarnings({ "HardcodedFileSeparator" })
    protected void trace(final @NotNull String userMsg, final Throwable cause) {
        final Log log1 = getLog();
        final AbstractTask model = getModel();
        final String msg;
        if (model == null)
            msg = userMsg;
        else {
            final TaskState state = model.getState();
            final String descr = model.getDescription();
            msg = "TA of AbstractTask " + model.getId() + (state == null ? "" : '/' + state.toString()) + ':'
                    + (userMsg.length() > 0 ? ' ' : "") + userMsg
                    + (descr == null ? "" : " DESCR: '" + descr + '\'');
        }
        if (cause == null)
            log1.trace(msg);
        else
            log1.trace(msg, cause);

    }

    /**
     * Inform all listeners about the current model.
     */
    protected void refreshTaskResource() {

        try {
            getPostponedActions().getListener().onModelChange(getModel());
        } catch (RuntimeException e) {
            // intentionally ignored
        }
    }

    /**
     *
     * Retrieves the AbstractTask from the database, which corresponds to {@code getModel()).
     * It is marked as done, by invoking {@code setDone(true)} on it and restored to the database.
     *
     * It will be set as {@code this}' the new model by calling {@code setModelAndBackup()}, which also makes a backup
     * of the new model.
     *
     */
    private void markAsDone() {
        final @NotNull AbstractTask model = getModel();
        if (!(getCancelled() || model.isDone())) {
            final EntityManager em = getEntityManager();
            try {
                em.getTransaction().begin();
                AbstractTask newModel = em.find(AbstractTask.class, getModel().getId());
                newModel.setDone(true);
                em.getTransaction().commit();
                setModelAndBackup(newModel);
            } catch (RuntimeException e) {
                trace("Non throwable exception: ", e);
            } finally {
                try {
                    if (em.getTransaction().isActive())
                        em.getTransaction().rollback();
                } catch (RuntimeException e) {
                    trace("Non throwable exception: ", e);
                }
            }
        }
    }

    /**
     * Retrieves the current model and checks if its lifetime is already exceeded.
     * In this case the model's TaskState will be set to {@code failed} and a {@link FailedException} is thrown,
     * which will stop the the main loop in {@code execute()}.
     *
     * Otherwise, the model's TaskState is set to {@code newState} and {@link #transit(TaskState, AbstractTask)}
     * will be invoked,
     * which calls a method corresponding to the TaskState {@code newState} and throws a {@code TransitException} after its
     * execution specifying the next TaskSate value for the model.
     *
     * The model is made persistent by the EntityManager and the changed model will be 
     * commited to the database.
     * If something goes wrong while commiting the new model, a stable rollback is assured.
     *
     * @param newState the new TaskState for the model
     */
    @SuppressWarnings({ "CaughtExceptionImmediatelyRethrown", "ThrowableInstanceNeverThrown" })
    private void transit(final TaskState newState) {

        if (getCancelled()) {
            log.debug("cancelled");
            throw new StopException(TaskState.IN_PROGRESS_UNKNOWN);
        }

        EntityManager em = getEntityManager();
        @NotNull
        AbstractTask model = getModel();

        // this throws a stop exception on timeout
        if (!(TaskState.FINISHED.equals(newState) || TaskState.FAILED.equals(newState)))
            checkTimeout(model, em);

        try {
            em.getTransaction().begin();
            if (newState != null) {
                if (!em.contains(model)) {
                    log.debug("model not in EntityManager");
                    try {
                        try {
                            em.persist(model);
                        } catch (EntityExistsException e) {
                            log.debug("persisting failed merging", e);
                            rewindTransaction(em.getTransaction());
                            em.merge(model);
                        }
                    } catch (RuntimeException e2) {
                        log.debug("probably persisting and merging failed", e2);
                        rewindTransaction(em.getTransaction());
                        final @NotNull AbstractTask newModel = em.find(AbstractTask.class, model.getId());
                        model.mold(newModel);
                        newModel.refresh(em);
                        setModel(newModel);
                        model = getModel();
                        log.debug("completed renewing of model");
                    }
                }
            }

            model.transit(newState);

            boolean committed = false;
            try {
                em.getTransaction().commit();
                //                em.flush( );
                committed = true;
            } catch (Exception e) {
                log.debug("commit of transit (" + newState.name() + ") model failed ", e);
                try {
                    rewindTransaction(em.getTransaction());
                    // if this point is reached s.th. is terribly foobared
                    // restore backup and fail
                    model = Copier.copy(false, backup);
                    em.merge(model);
                    // backup should be clean so commit mustn't fail.
                    model.fail(e);
                    em.getTransaction().commit();
                } catch (Exception e2) {
                    log.debug("restoring previous version failed", e2);
                    try {
                        // refresh em for final commit
                        EntityManagerAux.rollbackAndClose(em);
                    } catch (RuntimeException e3) {
                        log.debug("rollback old em failed", e3);
                    }

                    EntityManager nem = emf.createEntityManager();
                    TxFrame tx = new TxFrame(nem);
                    try {
                        log.debug("loading fresh model");
                        model = nem.find(model.getClass(), backup.getId());
                        boolean unkown = (model == null);
                        model = Copier.copy(false, backup);
                        model.fail(e2);
                        if (unkown)
                            nem.persist(model);
                        //     else
                        //         nem.merge( model );
                        tx.commit();
                        setModel(model);
                    } catch (RuntimeException e3) {
                        log.debug("exception during refresh: ", e3);
                        throw e3;
                    } finally {
                        tx.finish();
                        setOwnEntityManager(nem);
                        em = nem;
                    }
                }
            }

            // if model could be commited it has a clean state
            // refresh backup
            if (committed)
                setBackup(Copier.copy(false, model));

            final TaskState modelState = model.getState();
            refreshTaskResource();
            //noinspection HardcodedFileSeparator
            trace("on" + modelState + "()", null);
            transit(modelState, model);
        }
        // for debugging
        catch (RuntimeException e) {
            throw e;
        } finally {
            try {
                // for debuggin'
                boolean ta = em.getTransaction().isActive();
                if (ta) {
                    log.debug("final rollback");
                    em.getTransaction().rollback();
                }
            } catch (Exception e) {
                log.debug("final rollback failed", e);
            }
        }
    }

    /**
     * Invokes a rollback on an entity transaction and a following {@code begin()},
     * only if it has been marked (using {@code setRollbackOnly()}).
     *
     * @param txParam a transaction to be rewinded
     */
    private void rewindTransaction(final EntityTransaction txParam) {
        if (txParam.isActive()) {
            if (txParam.getRollbackOnly()) {
                txParam.rollback();
                txParam.begin();
            }
        } else
            txParam.begin();
    }

    /**
     * This method is used to control and change the Taskstate of an AbstractTask.
     *
     * It checks if the {@code model}'s TaskState can be set to {@code newState}.
     * If it is allowed (according to the order given in {@link TaskState}),
     * a method corresponding to the value of {@code newState} will be invoked.
     * 
     * All these methods must throw a {@code TransitException} to specify the new desired state.
     *
     *
     * @see TaskState
     * @see #onCreated(AbstractTask)
     * @see #onInitialized(AbstractTask)
     * @see #onUnknown(AbstractTask)
     * @see #onInProgress(AbstractTask)
     * @see #onFailed(AbstractTask) 
     * @see #onFinished(AbstractTask)
     *
     * @param newState the state, the model's state should be changed to
     * @param model the model whose state should be changed.
     */
    @SuppressWarnings({ "ThrowableInstanceNeverThrown" })
    private void transit(final @NotNull TaskState newState, final @NotNull AbstractTask model) {
        switch (model.getState().transit(newState)) {
        case CREATED:
            onCreated(model);
            break;
        case CREATED_UNKNOWN:
            onUnknown(model);
            break;
        case INITIALIZED:
            onInitialized(model);
            break;
        case INITIALIZED_UNKNOWN:
            onUnknown(model);
            break;
        case IN_PROGRESS:
            onInProgress(model);
            break;
        case IN_PROGRESS_UNKNOWN:
            onUnknown(model);
            break;
        case FAILED:
            if (!model.isDone())
                onFailed(model);
            /* safety catch-all */
            throw new StopException(TaskState.FAILED);
        case FINISHED:
            if (!model.isDone())
                onFinished(model);
            /* safety catch-all */
            throw new StopException(TaskState.FINISHED);
        default:
            throw new IllegalStateException("Invalid or unsupported task state");
        }
    }

    /**
     * This method will be called by {@link #transit(TaskState)},
     * if the model's state is unknown (so {@code CREATED_UNKNOWN}, {@code INITIALIZED_UNKNOWN} or {@code IN_PROGRESS_UNKNOWN}).
     *
     * It throws an UnsupportedOperationException.
     * 
     * @param model the model, whose TaskState is set to unknown.
     *
     * @return it throws an UnsupportedOperationException
     */
    protected TaskState onUnknown(final AbstractTask model) {
        throw new UnsupportedOperationException();
    }

    /**
     * This method will be called by {@link #transit(TaskState)},
     * if the model's TaskState is set to {@code CREATED}.
     *
     * It throws a {@code TransitException} with {@code INITIALIZED} as its TaskState.
     * This signalizes to change the model's TaskState to {@code INITIALIZED}.
     *
     *
     * @param model the model, whose TaskState is set to {@code CREATED}
     */
    protected void onCreated(final AbstractTask model) {
        transitToState(TaskState.INITIALIZED);

    }

    /**
     * This method will be called by {@link #transit(TaskState)},
     * if the model's TaskState is set to {@code INITIALIZED}.
     *
     * It throws a {@code TransitException} with {@code IN_PROGRESS} as its TaskState.
     * This signalizes to change the model's TaskState to {@code IN_PROGRESS}.
     *
     * @param model the model, whose TaskState is set to {@code INITIALIZED}
     */
    protected void onInitialized(final AbstractTask model) {
        transitToState(TaskState.IN_PROGRESS);
    }

    /**
     * This method will be called by {@link #transit(TaskState)},
     * if the model's TaskState is set to {@code IN_PROGRESS}.
     *
     * Define here what the action on the model, when its {@code TaskState} is set to {@code IN_PROGRESS}.
     *
     * An implementation of this method must throw a {@code TransitException} to define to which state the model
     * shall be set to.
     *
     * @param model the model, whose TaskState is set to {@code IN_PROGRESS}
     */
    protected abstract void onInProgress(final @NotNull AbstractTask model);

    /**
      * This method will be called by {@link #transit(TaskState)},
      * if the model's TaskState is set to {@code FAILED}.
      *
      * It makes a cleanup by calling {@code tryCleanup()} and calls {@link #stop(AbstractTask)} afterwards.
      *
      * @param model the model, whose TaskState is set to {@code FAILED}
      */
    protected final void onFailed(final @NotNull AbstractTask model) {
        tryCleanup(model);
        stop(model);
    }

    /**
     * This method will be called by {@link #transit(TaskState)},
     * if the model's TaskState is set to {@code FINISHED}
     *
     * It calls {@link #stop(AbstractTask)} 
     *
     * @param model the model, whose TaskState is set to {@code FINISHED}
     */
    protected void onFinished(final @NotNull AbstractTask model) {
        stop(model);
    }

    /**
     * Throws a new {@code TransitException} with {@code newState] as its TaskState.
     *
     * Note: It does not check if the state change is allow (according to the given order by {@link TaskState}.
     *
     * @param newState the new desired TaskState for the model belonging to this
     */
    protected static void transitToState(final @NotNull TaskState newState) {
        throw new TransitException(newState);
    }

    /**
     * Use this method to signalize, that an error occured during the model's computation.
     *
     * It throws a {@link FailedException} containing the RuntimeException {@code e}.
     *
     * Sets the progress of the AbstractTask to {@code zero} and stores the Exception in
     * the model's failure String {@link AbstractTask#getFaultString()}.
     * The data of the AbstractTask will be set to {@code e} and the TaskState is set to {@code FAILED}.
     *
     * @param e a RuntimeException which occured during the model's computation
     */
    protected void fail(final @NotNull RuntimeException e) {
        getModel().fail(e);
        //e.fillInStackTrace();
        // getLog().info("About to transit(FAIL) due to:", e);
        throw new FailedException(e);
    }

    /**
     * Use this method to signalize, that an model's computation is finished.
     *
     * It throws a {@link FinishedException} containing the result of the computation.
     *
     * It sets the progress of the AbstractTask to {@link AbstractTask#maxProgress} and stores the result in
     * {@link de.zib.gndms.model.gorfx.AbstractTask#getData()}.
     *
     * The model's TaskState is set to {@code FINISHED}.
     *
     * @param result the result of the the model's computation
     */
    protected void finish(final Serializable result) {
        getModel().finish(result);
        throw new FinishedException(result);
    }

    /**
     * Throws a StopException with and stores the model's current state in the Exception
     *
     * @param model an AbstractTask, which has to be stopped.
     */
    @SuppressWarnings({ "MethodMayBeStatic" })
    protected void stop(final @NotNull AbstractTask model) {
        throw new StopException(model.getState());
    }

    /**
     * If the {@code TaskExecutionService} this TaskAction runs on, is terminating or already terminated,
     * a {@link ShutdownTaskActionException} is thrown.
     *
     * @param e an InterruptedException, which will be passed to the {@code ShutdownTaskActionException}
     */
    protected final void shutdownIfTerminating(InterruptedException e) {
        if (getService().isTerminating())
            throw new ShutdownTaskActionException(e);
    }

    @SuppressWarnings({ "MethodMayBeStatic" })
    protected final void honorOngoingTransit(RuntimeException e) {
        if (isTransitException(e))
            throw e;
    }

    public static boolean isTransitException(Exception e) {
        return e instanceof TransitException;
    }

    public static boolean isFinishedTransition(Exception e) {
        return e instanceof FinishedException;
    }

    public static boolean isFailedTransition(Exception e) {
        return e instanceof FailedException;
    }

    public Log getLog() {
        return log;
    }

    public void setLog(final Log logParam) {
        //log = logParam;
    }

    protected AbstractTask getBackup() {
        return backup;
    }

    protected void setBackup(AbstractTask backup) {
        this.backup = backup;
    }

    /**
     * Stopps the computation of the model if the task lifetime is already exceeded.
     *
     * If the lifetime is exceeded, it sets the TaskState of the given model as well as the corresponding AbstractTask
     * in the database (if still present) to {@code FAILURE} by invoking {@code fail()}.
     *
     * @see #fail(RuntimeException)
     * @see AbstractTask#fail(Exception)
     *  
     * @param model a task with a termination time
     * @param em the entityManager storing the model
     */
    private void checkTimeout(@NotNull AbstractTask model, @NotNull EntityManager em) {

        // check the task lifetime
        if (model.getTerminationTime().compareTo(new GregorianCalendar()) < 1) {
            getLog().debug("Task lifetime exceeded");
            TxFrame tx = new TxFrame(em);
            boolean containt = false;
            try {
                // check if model is still there
                containt = em.contains(model);
                if (containt) {
                    model.fail(new RuntimeException("Task lifetime exceeded"));
                    getLog().debug("Try to persist task");
                }
                tx.commit();
            } catch (Exception e) {
                // exception here  doesn't really matter
                // task is doomed anyway
                getLog().warn(e);
            } finally {
                tx.finish();
            }
            // interrupt this thread
            getLog().debug("Stopping task thread");
            //Thread.currentThread().interrupt();
            if (containt)
                refreshTaskResource();
            fail(new RuntimeException("Task lifetime exceeded"));
        }
    }

    @Override
    public EntityManager getEntityManager() {
        return getOwnEntityManager();
    }

    public EntityManagerFactory getEmf() {
        return emf;
    }

    @Inject
    public void setEmf(final @NotNull EntityManagerFactory emfParam) {
        emf = emfParam;
    }

    public ConfigletProvider getConfigletProvider() {
        return configletProvider;
    }

    @Inject
    public void setConfigletProvider(ConfigletProvider configletProvider) {
        this.configletProvider = configletProvider;
    }

    /**
     * Tries to call the {@link #cleanUpOnFail(de.zib.gndms.model.gorfx.AbstractTask)} } method for the model,
     * catches and logs possible exceptions.
     *
     * @param model The model, on which a cleanup must be done.
     */
    public void tryCleanup(@NotNull AbstractTask model) {

        try {
            cleanUpOnFail(model);
        } catch (Exception e) {
            // don' throw them again
            getLog().debug("Exception on task cleanup: " + e.toString());
        }
    }

    /**
     * Extracts the actual permissions from a tasks permission info object
     *
     * @return
     */
    public FilePermissions actualPermissions() {

        if (getModel().getPermissions() != null) {
            PermissionConfiglet pc = configletProvider.getConfiglet(PermissionConfiglet.class,
                    getModel().getPermissions().getPermissionConfigletName());
            if (pc != null)
                return pc.permissionsFor(getModel().getPermissions().getUserName());
        }

        return null;
    }

    public void setCredentialProvider(CredentialProvider cp) {
        this.credentialProvider = cp;
    }

    public CredentialProvider getCredentialProvider() {

        return this.credentialProvider;
    }
}