001 /**
002 * Copyright 2004-2012 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.common.threads;
017
018 import java.lang.Thread.UncaughtExceptionHandler;
019
020 import org.kuali.common.threads.listener.ProgressNotifier;
021
022 /**
023 * Handles the execution of threads. The executeThreads() will start all threads and then block until they have
024 * completed. If an exception occurs in a client thread the uncaughtException() method of this ThreadHandler will be
025 * invoked. The default behavior if that happens is to set the stopThreads flag to true and retain a handle to the
026 * exception. Client threads should examine the isStopThreads() method of their handler and shutdown as quickly as
027 * possible if that method returns true.
028 *
029 * The default behavior if an exception occurs in a client thread is for the handler to re-throw it once client threads
030 * have completed. If that is not desired, set the rethrowException flag to false. The getException() method can still
031 * be used to obtain the exception thrown in the client thread.
032 *
033 * @param <T>
034 */
035 public class ThreadHandler<T> implements UncaughtExceptionHandler {
036
037 ThreadGroup group;
038 Thread[] threads;
039 ThreadHandlerException exception;
040 boolean stopThreads;
041 int threadCount;
042 ProgressNotifier<T> notifier;
043 boolean rethrowException = true;
044 ExecutionStatistics executionStatistics;
045
046 public ThreadGroup getGroup() {
047 return group;
048 }
049
050 public void setGroup(ThreadGroup group) {
051 this.group = group;
052 }
053
054 public Thread[] getThreads() {
055 return threads;
056 }
057
058 public void setThreads(Thread[] threads) {
059 this.threads = threads;
060 }
061
062 public void executeThreads() {
063 long start = System.currentTimeMillis();
064 start();
065 join();
066 long millis = System.currentTimeMillis() - start;
067 executionStatistics = new ExecutionStatistics();
068 executionStatistics.setExecutionTime(millis);
069 executionStatistics.setThreadCount(threadCount);
070 executionStatistics.setIterationCount(notifier.getProgress());
071
072 if (isThrowException()) {
073 throw exception;
074 }
075 }
076
077 protected boolean isThrowException() {
078 return rethrowException && exception != null;
079 }
080
081 protected void start() {
082 for (Thread thread : threads) {
083 thread.start();
084 }
085 }
086
087 protected void join() {
088 try {
089 for (Thread thread : threads) {
090 thread.join();
091 }
092 } catch (InterruptedException e) {
093 throw new ThreadHandlerException(e);
094 }
095 }
096
097 public synchronized void uncaughtException(Thread t, Throwable e) {
098 this.stopThreads = true;
099 this.group.interrupt();
100 long id = t.getId();
101 String name = t.getName();
102 this.exception = new ThreadHandlerException("Exception in thread [" + id + ":" + name + "]", e);
103 }
104
105 public synchronized boolean isStopThreads() {
106 return stopThreads;
107 }
108
109 public ThreadHandlerException getException() {
110 return exception;
111 }
112
113 public int getThreadCount() {
114 return threadCount;
115 }
116
117 public void setThreadCount(int threadCount) {
118 this.threadCount = threadCount;
119 }
120
121 public ProgressNotifier<T> getNotifier() {
122 return notifier;
123 }
124
125 public void setNotifier(ProgressNotifier<T> notifier) {
126 this.notifier = notifier;
127 }
128
129 public boolean isRethrowException() {
130 return rethrowException;
131 }
132
133 public void setRethrowException(boolean rethrowException) {
134 this.rethrowException = rethrowException;
135 }
136
137 public ExecutionStatistics getExecutionStatistics() {
138 return executionStatistics;
139 }
140 }