jaxx.runtime.swing.application.ActionExecutor Maven / Gradle / Ivy
/*
* #%L
* JAXX :: Runtime
*
* $Id: ActionExecutor.java 2225 2011-02-19 20:15:00Z tchemit $
* $HeadURL: https://nuiton.org/svn/jaxx/tags/jaxx-2.8.5/jaxx-runtime/src/main/java/jaxx/runtime/swing/application/ActionExecutor.java $
* %%
* Copyright (C) 2008 - 2010 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
* .
* #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 tchemit
* @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