All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.runtime.swing.wizard.WizardOperationActionThread Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * *##% 
 * JAXX Runtime
 * Copyright (C) 2008 - 2009 CodeLutin
 *
 * This program is free software: you can redistribute it and/or modify
 * it 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * ##%*
 */
package jaxx.runtime.swing.wizard;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date;
import javax.swing.SwingWorker.StateValue;
import jaxx.runtime.JAXXContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Thread qui réalise les opérations.
 *
 * Pour exécuter une nouvelle opération, on utilise la méthode
 * {@link #launchOperation(WizardOperationStep)}.
 *
 * Note: Pour bloquer (ou débloquer) le thread, on utilise la méthode {@link #setWaiting(boolean)}
 * 
 * @param  le type des etapes
 * @param  le type de modele
 * @param  le type d'action d'operation
 * 
 * @author tony
 * @since 1.3
 */
public abstract class WizardOperationActionThread, A extends WizardOperationAction> extends Thread implements PropertyChangeListener {

    /** to use log facility, just put in your code: log.info(\"...\"); */
    private static final Log log = LogFactory.getLog(WizardOperationActionThread.class);
    /**
     * l'état du thread si annulé
     */
    private boolean canceled;
    protected Class modelClass;
    protected A currentAction;
    /**
     * un lock pour permettre la suspension et la reprise du thread
     * lors du mode interactif.
     */
    private final Object LOCK = new Object();

    protected abstract M getModel();

    protected abstract JAXXContext getContext();

    public WizardOperationActionThread(Class modelClass) throws IllegalArgumentException {
        super(WizardOperationActionThread.class.getSimpleName() + " " + new Date());
        this.modelClass = modelClass;
    }

    public void cancel() {
        log.info("cancel " + this);
        this.canceled = true;
        
        // on annule le modele
        getModel().cancel();
                
        // on rend la main au thread
        setWaiting(false);
    }

    @SuppressWarnings("unchecked")
    public A launchOperation(E operation) {

        if (currentAction != null && (!currentAction.isDone() || currentAction.operationState == WizardOperationState.RUNNING)) {
            // on ne peut traiter qu'une seule opération à la fois
            throw new IllegalStateException("can not add a operation when thread is busy, or has another operation to be done");
        }
        currentAction = (A) getModel().getOperationAction(operation);

        // on libere le thread pour qu'il execute l'opération
        setWaiting(false);

        return currentAction;
    }

    public A getCurrentAction() {
        return currentAction;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        log.trace(evt.getPropertyName() + " <" + evt.getOldValue() + " - " + evt.getNewValue() + ">");
        if ("state".equals(evt.getPropertyName())) {
            StateValue state = (StateValue) evt.getNewValue();
            if (state == StateValue.DONE) {
                // on rend la main au thread pour qu'il attende une prochaine operation
                setWaiting(false);
            }
        }
    }

    @Override
    public void run() {
        try {

            // on vérifie que le context contient bien le modèle
            if (getModel() == null) {
                throw new NullPointerException("could not find model " + modelClass + " for " + this);
            }

            while (!canceled) {

                if (canceled) {
                    // une annulation a été demandé
                    // donc même si une opération est demandée, on ne la traite
                    // pas
                    break;
                }

                // en attente qu'une opération
                // le block est bloqué jusqu'à arrivée d'une opération
                // ou une demande d'annulation
                setWaiting(true);

                // le thread a repris la main, donc plus en attente
                log.trace("no more waiting " + this);

                if (!canceled) {
                    // une opération a été demandée

                    // le thread écoute les modifications de l'action
                    currentAction.addPropertyChangeListener(this);

                    // l'opération passe en etant en cours
                    getModel().setOperationState(WizardOperationState.RUNNING);

                    // démarrage de l'opération dans un worker
                    currentAction.start(getContext());
                    // le thread est bloqué jusqu'à la fin de l'opération
                    // ou une demande d'annulation
                    setWaiting(true);

                    // le thread reprend la main des que l'operation
                    // est terminée ou a été annulée, on passera alors
                    // dans la méthode onPropertyChanged

                    if (canceled) {
                        getModel().setOperationState(WizardOperationState.CANCELED);
                    } else {
                        getModel().setOperationState(currentAction.getOperationState());
                    }

                    // le thread n'écoute plus l'action car elle est terminée
                    // ou annulée
                    currentAction.removePropertyChangeListener(this);
                    // suppression de l'action
                    //currentAction = null;
                }

            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            unlockThread();
            log.trace(this + " will close...");
            close();
        }
    }

    /**
     * La méthode pour nettoyer le thread, a la fermeture.
     *
     */
    protected void close() {
        // par defaut, on ne fait rien
        log.trace(this);
    }

    protected void setWaiting(boolean waiting) {

        if (waiting && !canceled) {
            // locking thread
            try {
                lockThread();
            } catch (InterruptedException ex) {
                log.error(ex.getMessage(), ex);
                canceled = true;
            }
        }

        if (!waiting) {
            // release lock
            unlockThread();
        }
    }

    protected void lockThread() throws InterruptedException {
        synchronized (LOCK) {
            log.trace(this);
            //  lock
            LOCK.wait();
        }
    }

    protected void unlockThread() {
        synchronized (LOCK) {
            log.trace(this);
            // unlock
            LOCK.notify();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy