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    }