Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gui4j/core/Gui4jThreadManager.java


1   package org.gui4j.core;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.InvocationTargetException;
5   import java.util.LinkedList;
6   import java.util.Map;
7   
8   import javax.swing.SwingUtilities;
9   
10  import org.apache.commons.logging.Log;
11  import org.apache.commons.logging.LogFactory;
12  import org.gui4j.Gui4jCallBase;
13  import org.gui4j.Gui4jGetValue;
14  import org.gui4j.exception.ErrorTags;
15  import org.gui4j.exception.Gui4jUncheckedException;
16  
17  
18  /**
19   * The Thread Manager deals with worker threads used to perform GUI actions. The intention is to take a thread
20   * from a pool, use this thread to perform the necessary action and then put the thread back into the pool.
21   */
22  public final class Gui4jThreadManager implements ErrorTags, Serializable
23  {
24      protected static Log mLogger = LogFactory.getLog(Gui4jThreadManager.class);
25  
26      private LinkedList mThreadPool = new LinkedList();
27      private LinkedList mWorkPackages = new LinkedList();
28      protected boolean mHighPriorityThreadActive;
29      protected final Gui4jInternal mGui4j;
30      private int mFreeThreadCount;
31      private int mWorkPackageCount;
32      private int mWorkerCount;
33      private int mMaxNumberOfWorkerThreads;
34      private boolean mUseWorkerThreads;
35  
36      /**
37       * Constructor for Gui4jThreadManager.
38       * @param gui4j
39       * @param numberOfWorkerThreads
40       */
41      private Gui4jThreadManager(Gui4jInternal gui4j, int numberOfWorkerThreads)
42      {
43          super();
44          mGui4j = gui4j;
45          setNumberOfWorkerThreads(numberOfWorkerThreads);
46      }
47  
48      /**
49       * @param gui4j
50       * @param numberOfWorkerThreads
51       * @return a new instance of the Thread Manager. This method is used only by the class <code>Gui4j</code>.
52       */
53      public static Gui4jThreadManager getNewInstance(Gui4jInternal gui4j, int numberOfWorkerThreads)
54      {
55          return new Gui4jThreadManager(gui4j, numberOfWorkerThreads);
56      }
57  
58      /**
59       * Sets the maximum number of worker threads. The value <code>-1</code> represents an unlimited number
60       * of threads. Value <code>0</code> implies always using the Swing GUI Thread. Any number greater than
61       * <code>0</code> really sets the maximum number of worker threads. If there is work to do and no worker
62       * is free, then the work is put into a FIFO queue and handled when worker gets free.
63       * @param numberOfWorkerThreads
64       */
65      public void setNumberOfWorkerThreads(int numberOfWorkerThreads)
66      {
67          // it is not allowd to dynamically change the number of worker threads
68          assert mWorkerCount == 0;
69  
70          mMaxNumberOfWorkerThreads = numberOfWorkerThreads;
71          mUseWorkerThreads = mMaxNumberOfWorkerThreads != 0;
72      }
73  
74      /**
75       * Performs the given work. Dependant of the number of maximum worker threads, the work is either
76       * performed in the same thread, or by a new worker, or put into a FIFO queue.
77       * @param gui4jController
78       * @param work
79       * @param paramMap
80       */
81      public void performWork(final Gui4jCallBase gui4jController, final Gui4jGetValue[] work, final Map paramMap)
82      {
83          performWork(gui4jController, work, paramMap, null);
84      }
85  
86      /**
87       * Performs the given work. Dependant of the number of maximum worker threads, the work is either
88       * performed in the same thread, or by a new worker, or put into a FIFO queue.
89       * @param gui4jController
90       * @param work
91       * @param paramMap
92       * @param forceExecutionInCurrentThread
93       */
94      public void performWork(
95          final Gui4jCallBase gui4jController,
96          final Gui4jGetValue[] work,
97          final Map paramMap,
98          boolean forceExecutionInCurrentThread)
99      {
100         performWork(gui4jController, work, paramMap, null, forceExecutionInCurrentThread);
101     }
102 
103     public void performWork(
104         final Gui4jCallBase gui4jController,
105         final Gui4jGetValue[] work,
106         final Map paramMap,
107         final Gui4jComponentInstance actionHandler)
108     {
109         performWork(gui4jController, work, paramMap, actionHandler, false);
110     }
111 
112     public void performPriorityWork(
113         final Gui4jCallBase gui4jController,
114         final Gui4jGetValue[] work,
115         final Map paramMap,
116         final Gui4jComponentInstance actionHandler)
117     {
118         performPriorityWork(gui4jController, work, paramMap, actionHandler, false);
119     }
120 
121     /**
122      * Performs the given work. Dependant of the number of maximum worker threads, the work is either
123      * performed in the same thread, or by a new worker, or put into a FIFO queue.
124      * @param gui4jController
125      * @param work
126      * @param paramMap
127      * @param actionHandler
128      * @param forceExecutionInCurrentThread
129      */
130     public void performWork(
131         final Gui4jCallBase gui4jController,
132         final Gui4jGetValue[] work,
133         final Map paramMap,
134         final Gui4jComponentInstance actionHandler,
135         boolean forceExecutionInCurrentThread)
136     {
137         performWork(gui4jController, work, paramMap, actionHandler, forceExecutionInCurrentThread, false);
138     }
139 
140     /**
141      * Performs the given work. Dependant of the number of maximum worker threads, the work is either
142      * performed in the same thread, or by a new worker, or put into a FIFO queue.
143      * @param gui4jController
144      * @param work
145      * @param paramMap
146      * @param actionHandler
147      * @param forceExecutionInCurrentThread
148      */
149     public void performPriorityWork(
150         final Gui4jCallBase gui4jController,
151         final Gui4jGetValue[] work,
152         final Map paramMap,
153         final Gui4jComponentInstance actionHandler,
154         boolean forceExecutionInCurrentThread)
155     {
156         performWork(gui4jController, work, paramMap, actionHandler, forceExecutionInCurrentThread, true);
157     }
158 
159     /**
160      * Performs the given work. Dependant of the number of maximum worker threads, the work is either
161      * performed in the same thread, or by a new worker, or put into a FIFO queue.
162      * @param gui4jController
163      * @param work
164      * @param paramMap
165      * @param actionHandler
166      * @param forceExecutionInCurrentThread
167      * @param isHighPriorityThread
168      */
169     private void performWork(
170         final Gui4jCallBase gui4jController,
171         final Gui4jGetValue[] work,
172         final Map paramMap,
173         final Gui4jComponentInstance actionHandler,
174         boolean forceExecutionInCurrentThread,
175         final boolean isHighPriorityThread)
176     {
177         if (mUseWorkerThreads && SwingUtilities.isEventDispatchThread() && !forceExecutionInCurrentThread)
178         {
179             final InvokerCallStack callStack = mGui4j.traceWorkerInvocation() ? new InvokerCallStack(
180                 Thread.currentThread().getName()) : null;
181             // use another thread to perform task
182             Runnable run = new Runnable()
183             {
184                 public void run()
185                 {
186                     WorkerThread thread = getWorkerThread(
187                         gui4jController,
188                         work,
189                         paramMap,
190                         actionHandler,
191                         callStack,
192                         isHighPriorityThread);
193                     if (thread != null)
194                     {
195                         // mLogger.trace("Notifying " + thread);
196                         synchronized (thread)
197                         {
198                             thread.notify();
199                         }
200                     }
201                     else
202                     {
203                         mLogger.debug("Currently no free worker, work put on stack");
204                     }
205                 }
206             };
207             SwingUtilities.invokeLater(run);
208         }
209         else
210         {
211             for (int i = 0; i < work.length; i++)
212             {
213                 if (work[i] != null)
214                 {
215                     work[i].getValue(gui4jController, paramMap, null);
216                 }
217             }
218         }
219     }
220 
221     /**
222      * Perform the given work.
223      * @see org.gui4j.core.Gui4jThreadManager#performWork(Gui4jCallBase,Gui4jGetValue[],Map)
224      * @param gui4jController
225      * @param action
226      * @param paramMap
227      */
228     public void performWork(Gui4jCallBase gui4jController, Gui4jGetValue action, Map paramMap)
229     {
230         Gui4jGetValue[] work = { action };
231         performWork(gui4jController, work, paramMap);
232     }
233 
234     /**
235      * Perform the given work.
236      * @see org.gui4j.core.Gui4jThreadManager#performWork(Gui4jCallBase,Gui4jGetValue[],Map)
237      * @param gui4jController
238      * @param action
239      * @param paramMap
240      * @param forceExecutionInCurrentThread
241      */
242     public void performWork(
243         Gui4jCallBase gui4jController,
244         Gui4jGetValue action,
245         Map paramMap,
246         boolean forceExecutionInCurrentThread)
247     {
248         Gui4jGetValue[] work = { action };
249         performWork(gui4jController, work, paramMap, forceExecutionInCurrentThread);
250     }
251 
252     protected WorkerThread getWorkerThread(
253         Gui4jCallBase gui4jController,
254         Gui4jGetValue[] work,
255         Map paramMap,
256         Gui4jComponentInstance actionHandler,
257         InvokerCallStack callStack,
258         boolean isHighPriorityThread)
259     {
260         WorkerThread worker;
261         synchronized (mThreadPool)
262         {
263             if (!mHighPriorityThreadActive && mFreeThreadCount > 0)
264             {
265                 mFreeThreadCount--;
266                 worker = (WorkerThread) mThreadPool.removeFirst();
267                 worker.handleWork(
268                     gui4jController,
269                     work,
270                     paramMap,
271                     actionHandler,
272                     callStack,
273                     isHighPriorityThread);
274             }
275             else
276             {
277                 if (mWorkerCount == mMaxNumberOfWorkerThreads || mHighPriorityThreadActive)
278                 {
279                     mWorkPackages.add(new WorkPackage(
280                         gui4jController,
281                         work,
282                         paramMap,
283                         actionHandler,
284                         callStack,
285                         isHighPriorityThread));
286                     mWorkPackageCount++;
287                     worker = null;
288                 }
289                 else
290                 {
291                     worker = new WorkerThread(++mWorkerCount);
292                     worker.start();
293                     worker.handleWork(
294                         gui4jController,
295                         work,
296                         paramMap,
297                         actionHandler,
298                         callStack,
299                         isHighPriorityThread);
300                 }
301             }
302         }
303         return worker;
304     }
305 
306     protected void putThreadBackIntoPool(WorkerThread thread)
307     {
308         thread.cleanUp();
309         synchronized (mThreadPool)
310         {
311             if (mWorkPackageCount > 0)
312             {
313                 WorkPackage workPackage = (WorkPackage) mWorkPackages.removeFirst();
314                 mWorkPackageCount--;
315                 thread.handleWork(
316                     workPackage.mGui4jController,
317                     workPackage.mWork,
318                     workPackage.mParamMap,
319                     workPackage.mActionHandler,
320                     workPackage.mCallStack,
321                     workPackage.mIsHighPriorityThread);
322             }
323             else
324             {
325                 mFreeThreadCount++;
326                 mThreadPool.add(thread);
327             }
328         }
329     }
330 
331     public void clearThreadsInPool()
332     {
333         synchronized (mThreadPool)
334         {
335             mThreadPool.clear();
336             mFreeThreadCount = 0;
337         }
338     }
339 
340     private static class WorkPackage
341     {
342         public final Gui4jCallBase mGui4jController;
343         public final Gui4jGetValue[] mWork;
344         public final Gui4jComponentInstance mActionHandler;
345         public final Map mParamMap;
346         public final InvokerCallStack mCallStack;
347         public final boolean mIsHighPriorityThread;
348 
349         public WorkPackage(
350             Gui4jCallBase gui4jController,
351             Gui4jGetValue[] work,
352             Map paramMap,
353             Gui4jComponentInstance actionHandler,
354             InvokerCallStack callStack,
355             boolean isHighPriorityThread)
356         {
357             mGui4jController = gui4jController;
358             mWork = work;
359             mParamMap = paramMap;
360             mActionHandler = actionHandler;
361             mCallStack = callStack;
362             mIsHighPriorityThread = isHighPriorityThread;
363         }
364     }
365 
366     public class WorkerThread extends Thread
367     {
368 
369         private Gui4jCallBase mGui4jController;
370         private Gui4jGetValue[] mWork;
371         private Gui4jComponentInstance mActionHandler;
372         private Map mParamMap;
373         private boolean mWorkAvailable;
374         private final int mId;
375         private InvokerCallStack mCallStack;
376         private boolean mIsHighPriorityThread;
377 
378         public WorkerThread(int n)
379         {
380             mId = n;
381             mLogger.debug(toString() + ": created");
382             setName(toString());
383         }
384 
385         public Throwable getCallStack()
386         {
387             return mCallStack;
388         }
389 
390         public String toString()
391         {
392             return "Worker " + mId;
393         }
394 
395         protected void handleWork(
396             Gui4jCallBase gui4jController,
397             Gui4jGetValue[] work,
398             Map paramMap,
399             Gui4jComponentInstance actionHandler,
400             InvokerCallStack callStack,
401             boolean isHighPriorityThread)
402         {
403             mGui4jController = gui4jController;
404             mWork = work;
405             mParamMap = paramMap;
406             mActionHandler = actionHandler;
407             mWorkAvailable = true;
408             mCallStack = callStack;
409             mIsHighPriorityThread = isHighPriorityThread;
410         }
411 
412         public void cleanUp()
413         {
414             mGui4jController = null;
415             mWork = null;
416             mParamMap = null;
417             mActionHandler = null;
418             mCallStack = null;
419             mIsHighPriorityThread = false;
420         }
421 
422         public void run()
423         {
424             while (true)
425             {
426                 if (mWorkAvailable && !mHighPriorityThreadActive)
427                 {
428                     try
429                     {
430                         if (mIsHighPriorityThread)
431                         {
432                             mHighPriorityThreadActive = true;
433                         }
434                         // mLogger.trace(toString() + ": performing work");
435                         for (int i = 0; i < mWork.length; i++)
436                         {
437                             final Gui4jGetValue work = mWork[i];
438                             if (work == null)
439                             {
440                                 continue;
441                             }
442                             // mLogger.trace(toString() + ": performing work " +work);
443                             try
444                             {
445                                 work.getValueNoErrorChecking(mGui4jController, mParamMap, null);
446                                 // Falls der ActionHandler angegeben wurde, rufen wir
447                                 // (nur) nach dem ersten Aufruf die <code>handleSuccess</code>
448                                 // Methode auf.
449                                 // Beim Edit-Feld wird damit im Ok-Fall der Inhalt nochmals
450                                 // angezeigt. Außerdem kann damit Validierung gemacht werden.
451                                 if (i == 0 && mActionHandler != null)
452                                 {
453                                     mActionHandler.handleSuccess();
454                                 }
455                             }
456                             catch (Throwable t)
457                             {
458                                 // Analog zum Ok-Fall, rufen wir im Fehlerfall nach dem ersten
459                                 // Aufruf die <code>handleException</code> Methode auf. Damit
460                                 // kann beispielsweise Validierung gemacht werden.
461                                 if (i == 0 && mActionHandler != null)
462                                 {
463                                     mActionHandler.handleException(t);
464                                 }
465                                 else
466                                 {
467                                     // Falls kein ActionHandler definiert wurde, oder
468                                     // es sich nicht um den ersten Aufruf handelt,
469                                     // erfolgt die normale Fehlerbehandlung.
470                                     mGui4j.handleException(mGui4jController, t, null);
471                                 }
472                             }
473                         }
474                         // mLogger.trace(toString() + ": work finished");
475                         mWorkAvailable = false;
476                     }
477                     finally
478                     {
479                         if (mIsHighPriorityThread)
480                         {
481                             mHighPriorityThreadActive = false;
482                             mIsHighPriorityThread = false;
483                         }
484                     }
485                     putThreadBackIntoPool(this);
486                 }
487                 if (!mWorkAvailable || mHighPriorityThreadActive)
488                 {
489                     try
490                     {
491                         synchronized (this)
492                         {
493                             wait();
494                         }
495                         // mLogger.trace(toString() + ": got notified");
496                     }
497                     catch (InterruptedException e)
498                     {
499                         mLogger.error("Thread interrupted", e);
500                         return;
501                     }
502                 }
503             }
504         }
505 
506     }
507 
508     private static class InvokerCallStack extends Throwable
509     {
510         public InvokerCallStack(String threadName)
511         {
512             super(threadName);
513         }
514 
515         public String toString()
516         {
517             return "Thread [" + getMessage() + "]";
518         }
519 
520     }
521 
522     /**
523      * Insertes the given <code>Runnable</code> into the task queue of the GUI Thread. If the calling thread
524      * is the GUI thread, this call immediately returns. If the calling thread is not the GUI thread, this
525      * call does not return until the GUI thread has completed the task.
526      * @param run task to be scheduled in the GUI thread
527      */
528     public static void executeInSwingThreadAndWait(Runnable run)
529     {
530         try
531         {
532             if (SwingUtilities.isEventDispatchThread())
533             {
534                 // run.run();
535                 SwingUtilities.invokeLater(run);
536             }
537             else
538             {
539                 SwingUtilities.invokeAndWait(run);
540             }
541         }
542         catch (InterruptedException ex)
543         {
544             mLogger.warn("Interrupted", ex);
545         }
546         catch (InvocationTargetException ex)
547         {
548             Gui4jReflectionManager.handleInvocationTargetException(ex);
549             throw new Gui4jUncheckedException.ProgrammingError(
550                 PROGRAMMING_ERROR_invocation_target_exception,
551                 ex);
552         }
553     }
554 
555     /**
556      * The given task is executed in the GUI thread as soon as possible. There are two cases: <br>
557      * If this thread is not the GUI thread, the given <code>Runnable</code> is inserted into the task queue
558      * of the GUI thread. This call then returns immediately without waiting for the scheduled task to be
559      * finished. <br>
560      * If this thread is the GUI thread itself, the given task is executed immediately and synchronously, i.e.
561      * this call will not return until the task is completed.
562      * @param run task to be scheduled in the GUI thread
563      */
564     public static void executeInSwingThreadAndContinue(final Runnable run)
565     {
566         if (SwingUtilities.isEventDispatchThread())
567         {
568             run.run();
569         }
570         else
571         {
572             SwingUtilities.invokeLater(run);
573         }
574     }
575 }