KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ozoneDB > core > storage > gammaStore > AsyncExec


1 // You can redistribute this software and/or modify it under the terms of
2
// the Ozone Core License version 1 published by ozone-db.org.
3
//
4
// Copyright (C) 2003-@year@, Leo Mekenkamp. All rights reserved.
5
//
6
// $Id: AsyncExec.java,v 1.4 2004/02/01 20:55:47 leomekenkamp Exp $
7

8 package org.ozoneDB.core.storage.gammaStore;
9
10 import java.io.ByteArrayOutputStream JavaDoc;
11 import java.io.PrintStream JavaDoc;
12 import java.util.LinkedHashMap JavaDoc;
13 import java.util.Map JavaDoc;
14 import java.util.logging.Level JavaDoc;
15 import java.util.logging.Logger JavaDoc;
16
17 /**
18  * <p>Runs tasks in a separate thread. Tasks are identified by the keys used
19  * to put them in this AsyncExec. When a task has been run, it will be
20  * automatically removed from this instance.</p>
21  *
22  * <p>If a task throws a <code>Throwable</code> in its <code>run()</code> method,
23  * it will be caught and displayed; the thread handling all tasks will continue
24  * normally.</p>
25  *
26  * <p>For every instance of this class 1 thread is created, so keep this in
27  * mind when creating instances...</p>
28  *
29  *
30  * @author leo
31  */

32 public class AsyncExec {
33     
34     private static final Logger JavaDoc log = Logger.getLogger(AsyncExec.class.getName());
35     
36     private static final int NOT_STARTED = 0;
37     private static final int RUNNING = 1;
38     private static final int STOPPING = 2;
39     private static final int STOPPED = 3;
40     private LinkedHashMap JavaDoc _map = new LinkedHashMap JavaDoc();
41     private volatile int status = NOT_STARTED;
42     
43     /**
44      * key of the <code>Runnable</code> that is currently being processed, or
45      * has recently finished its <code>run()</code> method.
46      */

47     private volatile Object JavaDoc currentKey = null;
48     private ProcessorThread thread;
49     
50     public AsyncExec(String JavaDoc threadName, int priority, boolean useDaemonThread) {
51         thread = new ProcessorThread(threadName);
52         thread.setPriority(priority);
53         thread.setDaemon(useDaemonThread);
54         thread.start();
55         synchronized (this) {
56             while (getStatus() < RUNNING) {
57                 try {
58                     wait(1000);
59                 } catch (InterruptedException JavaDoc ignore) {
60                 }
61             }
62         }
63     }
64     
65     /**
66      * Puts a task into this instance. If a task is already present under that
67      * key it will be 'overwritten', just like in <code>java.util.Map</code>
68      * instances.
69      *
70      * @param key key used to identify the given task
71      * @param task task to be run
72      * @return Runnable the task already present under the given key
73      * @throws IllegalStateException if stopped or stopping
74      */

75     public synchronized Runnable JavaDoc put(Object JavaDoc key, Runnable JavaDoc task) {
76
77         // first make sure that if the task is running, it can finish first
78
remove(key);
79
80         checkStatus();
81         Runnable JavaDoc result = (Runnable JavaDoc) getMap().put(key, task);
82         notifyAll();
83         return result;
84     }
85
86     /**
87      * @throws IllegalStateException if stopped or stopping
88      */

89     private void checkStatus() {
90         switch (getStatus()) {
91         case NOT_STARTED:
92             throw new IllegalStateException JavaDoc("not started");
93             // break;
94
case RUNNING:
95             break;
96         case STOPPING:
97             throw new IllegalStateException JavaDoc("stopping");
98         case STOPPED:
99             throw new IllegalStateException JavaDoc("stopped");
100             // break;
101
default:
102             throw new IllegalStateException JavaDoc("It's worse than that: he's dead Jim.");
103         }
104     }
105
106     /**
107      * Returns the task that was put into this instance with the specified key.
108      * If the task with corresponding specified key is being run at the very
109      * moment of this call, then this method will block until the tasks
110      * <code>run()</code> method has completed.
111      *
112      * @param key key used to lookup the task
113      * @throws IllegalStateException if stopped or stopping
114      * @return task for specified key (<code>null</code> if no such key)
115      */

116     public synchronized Runnable JavaDoc get(Object JavaDoc key) {
117         checkStatus();
118         while (key.equals(getCurrentKey())) {
119             try {
120                 wait();
121             } catch (InterruptedException JavaDoc ignore) {
122             }
123         }
124         return (Runnable JavaDoc) getMap().get(key);
125     }
126     
127     /** Removes the task that was put into this instance with the specified key.
128      *
129      * @param key key used to lookup the task
130      * @throws IllegalStateException if stopped or stopping
131      * @return task for specified key (<code>null</code> if no such key)
132      */

133     public synchronized Runnable JavaDoc remove(Object JavaDoc key) {
134         if (key == null) {
135             throw new IllegalArgumentException JavaDoc("key must not be null");
136         }
137         while (key.equals(getCurrentKey())) {
138             if (log.isLoggable(Level.FINE)) log.fine(this + ": key is currentKey: " + getCurrentKey());
139             try {
140                 wait();
141             } catch (InterruptedException JavaDoc ignore) {
142             }
143         }
144         return (Runnable JavaDoc) getMap().remove(key);
145     }
146     
147     /**
148      * Returns the number of tasks currently in this instance. Note that this
149      * number will decrease over time (if no new tasks are put in) every time
150      * a task has completed.
151      *
152      * @return number of tasks
153      */

154     public synchronized int size() {
155         return getMap().size();
156     }
157
158     /**
159      * Tells this instance to stop; no more tasks can be put in or removed.
160      * Blocks until all tasks have completed.
161      */

162     public void stopWhenReady() {
163         synchronized (this) {
164             checkStatus();
165             setStatus(STOPPING);
166             notifyAll();
167         }
168         for (boolean interrupted = true; interrupted; ) {
169             try {
170                 thread.join();
171                 interrupted = false;
172             } catch (InterruptedException JavaDoc ignore) {
173             }
174         }
175     }
176     
177     private void setStatus(int status) {
178         this.status = status;
179     }
180     
181     private int getStatus() {
182         return status;
183     }
184     
185     private Object JavaDoc getCurrentKey() {
186         return currentKey;
187     }
188     
189     private void setCurrentKey(Object JavaDoc currentKey) {
190         this.currentKey = currentKey;
191     }
192     
193     private Map JavaDoc getMap() {
194         return _map;
195     }
196     
197     /**
198      * Handles all execution of tasks.
199      */

200     private class ProcessorThread extends Thread JavaDoc {
201         
202         public ProcessorThread(String JavaDoc name) {
203             super(name);
204         }
205         
206         public void run() {
207             synchronized (AsyncExec.this) {
208                 setStatus(RUNNING);
209                 AsyncExec.this.notifyAll();
210             }
211             if (log.isLoggable(Level.FINE)) log.fine(this + " started");
212             for (;;) {
213                 Runnable JavaDoc task;
214                 synchronized (AsyncExec.this) {
215                     
216                     // note that the first time we pass here the current key is
217
// _never_ in the map! See comment on currentKey.
218
if (log.isLoggable(Level.FINER)) log.finer("key has finished: " + getCurrentKey());
219                     getMap().remove(getCurrentKey());
220                     setCurrentKey(null);
221                     AsyncExec.this.notifyAll();
222                     while (getMap().size() == 0) {
223                         if (getStatus() == STOPPING) {
224                             setStatus(STOPPED);
225                             if (log.isLoggable(Level.FINE)) log.fine(this + " almost stopped");
226                             return;
227                         }
228                         try {
229                             // TODO: find out why this next call is needed (lockup if absent)
230
AsyncExec.this.notifyAll();
231                             AsyncExec.this.wait();
232                         } catch (InterruptedException JavaDoc ignore) {
233                         }
234                     }
235                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) getMap().entrySet().iterator().next();
236                     setCurrentKey(entry.getKey());
237                     task = (Runnable JavaDoc) entry.getValue();
238                 }
239                 try {
240                     if (log.isLoggable(Level.FINER)) log.finer("running task with key: " + getCurrentKey());
241                     task.run();
242                 } catch (Throwable JavaDoc t) {
243                     ByteArrayOutputStream JavaDoc buf = new ByteArrayOutputStream JavaDoc();
244                     PrintStream JavaDoc printStream = new PrintStream JavaDoc(buf);
245                     t.printStackTrace(printStream);
246                     printStream.close();
247                     log.severe(this + " has caught unhandled Throwable."
248                             + " Thread will NOT stop, Stacktrace will follow"
249                             + " both on stdout and here.\n" + buf.toString());
250                     t.printStackTrace();
251                 }
252             }
253         }
254         
255     }
256     
257 }
Popular Tags