| /******************************************************************************* |
| * Copyright (c) 2009, 2010 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; |
| |
| import java.util.concurrent.ThreadFactory; |
| |
| import org.eclipse.jpt.common.utility.Command; |
| |
| /** |
| * This command executor will dispatch commands to be executed in a separate |
| * thread, allowing calls to {@link CommandExecutor#execute(Command)} to return |
| * immediately. |
| * <p> |
| * <strong>NB:</strong> The client-supplied commands should handle any |
| * exception appropriately (e.g. log the exception and return gracefully) so |
| * the command execution thread can continue executing. |
| */ |
| public class AsynchronousCommandExecutor |
| implements StatefulCommandExecutor |
| { |
| /** |
| * This command queue is shared with the command execution/consumer thread. |
| * Adding a command to it will trigger the command to be executed by the |
| * command execution thread or, if another command is currently executing, |
| * to execute the new command once the currently executing command has |
| * finished executing. |
| */ |
| final SynchronizedQueue<Command> commands = new SynchronizedQueue<Command>(); |
| |
| /** |
| * Most of the thread-related behavior is delegated to this coordinator. |
| */ |
| private final ConsumerThreadCoordinator consumerThreadCoordinator; |
| |
| |
| // ********** construction ********** |
| |
| /** |
| * Construct an asynchronous command executor. |
| * Use simple JDK thread(s) for the command execution thread(s). |
| * Allow the command execution thread(s) to be assigned JDK-generated names. |
| */ |
| public AsynchronousCommandExecutor() { |
| this(SimpleThreadFactory.instance(), null); |
| } |
| |
| /** |
| * Construct an asynchronous command executor. |
| * Use the specified thread factory to construct the command execution thread(s). |
| * Allow the command execution thread(s) to be assigned JDK-generated names. |
| */ |
| public AsynchronousCommandExecutor(ThreadFactory threadFactory) { |
| this(threadFactory, null); |
| } |
| |
| /** |
| * Construct an asynchronous command executor. |
| * Use simple JDK thread(s) for the command execution thread(s). |
| * Assign the command execution thread(s) the specified name. |
| */ |
| public AsynchronousCommandExecutor(String threadName) { |
| this(SimpleThreadFactory.instance(), threadName); |
| } |
| |
| /** |
| * Construct an asynchronous command executor. |
| * Assign the command execution thread(s) the specified name. |
| */ |
| public AsynchronousCommandExecutor(ThreadFactory threadFactory, String threadName) { |
| super(); |
| this.consumerThreadCoordinator = this.buildConsumerThreadCoordinator(threadFactory, threadName); |
| } |
| |
| private ConsumerThreadCoordinator buildConsumerThreadCoordinator(ThreadFactory threadFactory, String threadName) { |
| return new ConsumerThreadCoordinator(this.buildConsumer(), threadFactory, threadName); |
| } |
| |
| private ConsumerThreadCoordinator.Consumer buildConsumer() { |
| return new Consumer(); |
| } |
| |
| |
| // ********** CallbackStatefulCommandExecutor implementation ********** |
| |
| /** |
| * Build and start the command execution/consumer thread. |
| * <p> |
| * Note: We don't clear the command queue here; so if a command has been |
| * added to the queue <em>before</em> getting here, the first command will |
| * be executed promptly (albeit, asynchronously). |
| * The command queue will be non-empty if:<ul> |
| * <li>{@link #execute(Command)} was called after the command executor was |
| * constructed but before {@link #start()} was called; or |
| * <li>{@link #execute(Command)} was called after {@link #stop()} was called |
| * but before {@link #start()} was called (to restart the command executor); or |
| * <li>{@link #stop()} was called when there were still outstanding commands |
| * remaining in the command queue |
| * </ul> |
| */ |
| public void start() { |
| this.consumerThreadCoordinator.start(); |
| } |
| |
| /** |
| * Put the specified command on the command queue, to be consumed by the |
| * command execution thread. |
| */ |
| public void execute(Command command) { |
| this.commands.enqueue(command); |
| } |
| |
| /** |
| * Interrupt the command execution thread so that it stops executing at the |
| * end of the current command. Suspend the current thread until |
| * the command execution thread is finished executing. If any uncaught |
| * exceptions were thrown while the execution thread was executing, |
| * wrap them in a composite exception and throw the composite exception. |
| */ |
| public void stop() { |
| this.consumerThreadCoordinator.stop(); |
| } |
| |
| |
| // ********** consumer ********** |
| |
| /** |
| * This implementation of {@link ConsumerThreadCoordinator.Consumer} |
| * will execute the commands enqueued by the asynchronous command executor. |
| * It will wait until the shared command queue is non-empty to begin executing the |
| * commands in the queue. Once a comand is executed, the thread will quiesce until |
| * another command is placed in the command queue. If a new command is |
| * enqueued during the execution of another command (either recursively by |
| * the command itself or by another thread), |
| * the new command will be executed immediately after the currently |
| * executing command is finished. |
| * Stop the thread by calling {@link Thread#interrupt()}. |
| */ |
| class Consumer |
| implements ConsumerThreadCoordinator.Consumer |
| { |
| Consumer() { |
| super(); |
| } |
| |
| /** |
| * Wait until a command has been placed in the queue. |
| */ |
| public void waitForProducer() throws InterruptedException { |
| AsynchronousCommandExecutor.this.commands.waitUntilNotEmpty(); |
| } |
| |
| /** |
| * Execute the first command in the queue and notify our listeners. |
| */ |
| public void execute() { |
| AsynchronousCommandExecutor.this.commands.dequeue().execute(); |
| } |
| |
| } |
| |
| } |