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

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

There is a newer version: 3.0.3
Show newest version
/*
 * #%L
 * JAXX :: Runtime Swing Application
 * %%
 * Copyright (C) 2008 - 2018 Code Lutin, Ultreia.io
 * %%
 * 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 org.nuiton.jaxx.runtime.swing.application;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.swing.SwingWorker;
import java.beans.PropertyChangeListener;
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;
import org.nuiton.jaxx.runtime.util.ReflectUtil;

/**
 * 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 = 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.
     */
    public void terminatesAndWaits() {

        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();

        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},
                (proxy, method, args) -> {
                    String methodName1 = method.getName();

                    if ("run".equals(methodName1)) {
                        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 (methodName1.equals("toString")) {
                        return toString();
                    }
                    if (methodName1.equals("equals")) {
                        return equals(args[0]);
                    }
                    if (methodName1.equals("hashCode")) {
                        return hashCode();
                    }
                    return null;
                }
        );
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy