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

jaxx.runtime.swing.application.ActionExecutor Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%L
 * JAXX :: Runtime
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * 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
 * .
 * #L%
 */
package jaxx.runtime.swing.application;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.util.ReflectUtil;

import javax.swing.SwingWorker;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Executor of {@link ActionWorker}.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.1
 */
public abstract class ActionExecutor {

    /** Logger */
    private static final Log log =
            LogFactory.getLog(ActionExecutor.class);

    /** current tasks */
    protected final Set> tasks =
            new HashSet>();

    /** the listener of running action */
    protected final PropertyChangeListener workerListener;

    /**
     * Hook when a action is about to start.
     *
     * @param source the action worker containing the action to perform
     */
    public abstract void onActionStart(ActionWorker source);

    /**
     * Hook when a action has failed.
     *
     * @param source the action worker containing the action to perform
     */
    public abstract void onActionFail(ActionWorker source);

    /**
     * Hook when a action has been canceled.
     *
     * @param source the action worker containing the action to perform
     */
    public abstract void onActionCancel(ActionWorker source);

    /**
     * Hook when a action has end with no failure or cancel.
     *
     * @param source the action worker containing the action to perform
     */
    public abstract void onActionEnd(ActionWorker source);

    /**
     * Hook atfer action is consumed.
     *
     * @param source the action worker containing the action to perform
     */
    public abstract void onAfterAction(ActionWorker source);

    public ActionExecutor() {
        workerListener = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (log.isDebugEnabled()) {
                    log.debug("action " + evt.getSource() + " property " +
                              evt.getPropertyName() + " changed <" +
                              evt.getOldValue() + " - " + evt.getNewValue() +
                              '>');
                }

                if ("state".equals(evt.getPropertyName())) {
                    ActionWorker source = (ActionWorker) evt.getSource();
                    SwingWorker.StateValue state =
                            (SwingWorker.StateValue) evt.getNewValue();

                    if (state == SwingWorker.StateValue.STARTED) {
                        // starting new action

                        onActionStart(source);
                        return;
                    }

                    if (state == SwingWorker.StateValue.DONE) {
                        // on rend la main au thread pour qu'il attende une
                        // prochaine operation

                        ActionWorker.ActionStatus status = source.getStatus();
                        if (log.isDebugEnabled()) {
                            log.debug("Action [" + source.getActionLabel() +
                                      "] status = " + status);
                        }
                        try {
                            switch (status) {

                                case OK:
                                    onActionEnd(source);
                                    break;
                                case CANCEL:
                                    onActionCancel(source);
                                    break;
                                case FAIL:
                                    onActionFail(source);
                                    break;
                            }
                        } finally {
                            tasks.remove(source);
                            onAfterAction(source);
                        }
                    }
                }
            }
        };
    }

    /**
     * Add an new action to perform.
     *
     * @param actionLabel the name of the action to perform
     * @param action      the action to perform
     * @return the worker that will launch the action
     */
    public ActionWorker addAction(String actionLabel, Runnable action) {

        ActionWorker worker;
        if (action instanceof ActionWorker) {

            worker = (ActionWorker) action;
        } else {

            worker = new ActionWorker(actionLabel, action);
        }
        worker.addPropertyChangeListener(workerListener);
        tasks.add(worker);
        worker.execute();
        return worker;
    }

    /**
     * Ask the thread to stop.
     *
     * It will finish all incoming files (but will not accept more tasks).
     *
     * Note: The method does not return until all tasks are not
     * consumed.
     *
     * @throws InterruptedException if something wrong while waiting end of
     *                              executor
     */
    public void terminatesAndWaits() throws InterruptedException {

        if (log.isDebugEnabled()) {
            log.debug("Executor " + this + " is terminating...");
        }

        // ask executor to terminate
        for (ActionWorker task : tasks) {
            task.cancel(true);
        }

        if (log.isDebugEnabled()) {
            log.debug("Executor " + this + " is terminated at " + new Date());
        }
    }

    public int getNbActions() {
        return getTasks().size();
    }

    public Set> getTasks() {
        return tasks;
    }

    /**
     * Creates a runnable instance (via a Proxy) to a method given by his name
     * ({@code methodName}) to invoke on {@code methodcontainer} with given
     * {@code arguments}.
     *
     * This is a great feature to create runnable code with a real context.
     *
     * @param methodContainer the container of the method to invoke
     * @param methodName      the name of the method to invoke
     * @param arguments       parameters to pass to method to invke.
     * @return the proxy instance
     */
    public Runnable createRunnable(final Object methodContainer,
                                   String methodName,
                                   final Object... arguments) {

        // find method

        Class klass = methodContainer.getClass();
        final Method targetMethod = ReflectUtil.getDeclaredMethod(klass,
                                                                  methodName,
                                                                  true,
                                                                  arguments
        );
        targetMethod.setAccessible(true);
        Runnable result;

        // create runnable proxy

        result = (Runnable) Proxy.newProxyInstance(
                getClass().getClassLoader(),
                new Class[]{Runnable.class},
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy,
                                         Method method,
                                         Object[] args) {
                        String methodName = method.getName();

                        if ("run".equals(methodName)) {
                            try {
                                if (log.isDebugEnabled()) {
                                    log.debug("will invoke run method");
                                }
                                return targetMethod.invoke(methodContainer, arguments);
                            } catch (IllegalAccessException e) {
                                throw new RuntimeException(
                                        "could not invoke on container " +
                                        methodContainer, e);
                            } catch (InvocationTargetException e) {
                                throw new RuntimeException(e);
                            }
                        }
                        if (methodName.equals("toString")) {
                            return toString();
                        }
                        if (methodName.equals("equals")) {
                            return equals(args[0]);
                        }
                        if (methodName.equals("hashCode")) {
                            return hashCode();
                        }
                        return null;
                    }
                }
        );
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy