blob: 98e973bd5a46503f167375532e529938d4f9b149 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.utility.internal.command;
import java.util.concurrent.ThreadFactory;
import org.eclipse.jpt.common.utility.ExceptionHandler;
import org.eclipse.jpt.common.utility.command.Command;
import org.eclipse.jpt.common.utility.command.NotifyingRepeatingCommand;
import org.eclipse.jpt.common.utility.internal.ConsumerThreadCoordinator;
import org.eclipse.jpt.common.utility.internal.ListenerList;
/**
* Extend the asynchronous repeating command to notify listeners
* when an execution "cycle" is complete; i.e. the command has,
* for the moment, handled every outstanding "execute" request and quiesced.
* This notification is <em>not</em> guaranteed to occur with <em>every</em>
* execution "cycle"; since other, unrelated, executions can be
* triggered concurrently.
*/
public class AsynchronousNotifyingRepeatingCommandWrapper
extends AsynchronousRepeatingCommandWrapper
implements NotifyingRepeatingCommand
{
private final ListenerList<Listener> listenerList = new ListenerList<Listener>(Listener.class);
/**
* This handles any exceptions thrown by the listeners.
*/
private final ExceptionHandler exceptionHandler;
// ********** construction **********
/**
* Construct an asynchronous notifying repeating command that executes the
* specified command.
* Allow the execution thread(s) to be assigned JDK-generated names.
*/
public AsynchronousNotifyingRepeatingCommandWrapper(Command command, ExceptionHandler exceptionHandler) {
super(command, exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
/**
* Construct an asynchronous notifying repeating command that executes the
* specified command.
* Assign the synchronization thread(s) the specified name.
*/
public AsynchronousNotifyingRepeatingCommandWrapper(Command command, String threadName, ExceptionHandler exceptionHandler) {
super(command, threadName, exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
/**
* Construct an asynchronous repeating command that executes the
* specified command.
* Use the specified thread factory to construct the synchronization thread(s).
* Assign the synchronization thread(s) the specified name.
* Any exceptions thrown by the consumer will be handled by the
* specified exception handler.
*/
public AsynchronousNotifyingRepeatingCommandWrapper(Config config) {
super(config);
this.exceptionHandler = config.getExceptionHandler();
}
/**
* Construct an asynchronous notifying repeating command that executes the
* specified command.
* Use the specified thread factory to construct the synchronization thread(s).
* Assign the synchronization thread(s) the specified name.
* Any exceptions thrown by the consumer will be handled by the
* specified exception handler.
*/
public AsynchronousNotifyingRepeatingCommandWrapper(Command command, ThreadFactory threadFactory, String threadName, ExceptionHandler exceptionHandler) {
super(command, threadFactory, threadName, exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
/**
* Build a consumer that will let us know when the execution thread has
* quiesced.
*/
@Override
ConsumerThreadCoordinator.Consumer buildConsumer(Command command) {
return new NotifyingConsumer(command);
}
// ********** NotifyingRepeatingCommand implementation **********
public void addListener(Listener listener) {
this.listenerList.add(listener);
}
public void removeListener(Listener listener) {
this.listenerList.remove(listener);
}
/**
* Notify our listeners.
*/
/* CU private */ void executionQuiesced() {
for (Listener listener : this.listenerList.getListeners()) {
try {
listener.executionQuiesced(this);
} catch (RuntimeException ex) {
// we could let the ConsumerThreadCoordinator handle these;
// but then the loop would be stopped with the first exception...
this.exceptionHandler.handleException(ex);
}
}
}
// ********** consumer **********
/**
* Extend {@link AsynchronousRepeatingCommandWrapper.Consumer}
* to notify the repeating command when execution has quiesced
* (i.e. the command has finished executing and there are no further
* requests for execution).
* Because execution is asynchronous, no other thread will be able to
* initiate another execution until the command's listeners have been
* notified. Note also, the command's listeners can, themselves,
* trigger another execution (by directly or indirectly calling
* {@link org.eclipse.jpt.common.utility.command.Command#execute()});
* but this execution will not occur until <em>after</em> all the
* listeners have been notified.
*/
/* CU private */ class NotifyingConsumer
extends Consumer
{
NotifyingConsumer(Command command) {
super(command);
}
@Override
public void consume() {
super.consume();
// hmmm - we will notify listeners even when we our thread is "interrupted";
// that seems ok... ~bjv
if (AsynchronousNotifyingRepeatingCommandWrapper.this.executeFlag.isFalse()) {
AsynchronousNotifyingRepeatingCommandWrapper.this.executionQuiesced();
}
}
}
}