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 - 2020 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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.runtime.swing.application.event.ActionExecutorEvent;
import org.nuiton.jaxx.runtime.swing.application.event.ActionExecutorListener;
import org.nuiton.jaxx.runtime.util.ReflectUtil;

import javax.swing.SwingWorker;
import javax.swing.event.EventListenerList;
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 java.util.function.BiConsumer;

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

    private static final Logger log = LogManager.getLogger(ActionExecutor.class);

    /**
     * Cuurrent tasks.
     */
    protected final Set> tasks;
    /**
     * The listener of running action.
     */
    protected final PropertyChangeListener workerListener;
    /**
     * Event listener list.
     */
    private final transient EventListenerList listenerList;

    public ActionExecutor() {
        this.tasks = new HashSet<>();
        this.listenerList = new EventListenerList();
        this.workerListener = evt -> {
            log.debug(String.format("action %s property %s changed <%s - %s>", evt.getSource(), evt.getPropertyName(), 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();
                    log.debug(String.format("Action [%s] status = %s", source.getActionLabel(), 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);
        executeWorker(actionLabel, worker);
        return worker;
    }

    /**
     * Add an new worker to perform.
     *
     * @param worker the worker to run
     */
    public void addAction(ActionWorker worker) {
        addAction(worker.getActionLabel(), worker);
    }

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

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

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

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

    /**
     * Hook after action is consumed.
     *
     * @param source the action worker containing the action to perform
     */
    public void onAfterAction(ActionWorker source) {
        fireActionDone(source);
    }

    /**
     * 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() { log.debug(String.format("Executor %s is terminating...", this)); // ask executor to terminate for (ActionWorker task : tasks) { task.cancel(true); } log.debug(String.format("Executor %s was terminated at %s", this, new Date())); } public void addActionExecutorListener(ActionExecutorListener listener) { log.info(String.format("adding listener %s", listener)); listenerList.add(ActionExecutorListener.class, listener); } public void removeActionExecutorListener(ActionExecutorListener listener) { log.info(String.format("removing listener %s", listener)); listenerList.remove(ActionExecutorListener.class, listener); } protected ActionExecutorListener[] getActionExecutorListener() { return listenerList.getListeners(ActionExecutorListener.class); } protected void fireActionStart(ActionWorker source) { fireAction(source, ActionExecutorListener::actionStart); } protected void fireActionFail(ActionWorker source) { fireAction(source, ActionExecutorListener::actionFail); } protected void fireActionCancel(ActionWorker source) { fireAction(source, ActionExecutorListener::actionCancel); } protected void fireActionEnd(ActionWorker source) { fireAction(source, ActionExecutorListener::actionEnd); } protected void fireActionDone(ActionWorker worker) { fireAction(worker, ActionExecutorListener::actionDone); } protected void fireAction(ActionWorker worker, BiConsumer function) { ActionExecutorEvent evt = new ActionExecutorEvent(this, worker); for (ActionExecutorListener listener : getActionExecutorListener()) { function.accept(listener, evt); if (evt.isConsumed()) { break; } } } 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; } /** * Execute worker once it was added to tasks. *

* This method permits to override how to execute it. * * @param actionLabel action label * @param worker worker to execute */ protected void executeWorker(String actionLabel, ActionWorker worker) { worker.execute(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy