1 package org.lsst.ccs.utilities.dispatch;
2
3
4 import java.util.concurrent.CopyOnWriteArrayList;
5 import java.util.concurrent.ExecutorService;
6 import java.util.logging.Level;
7
8 /**
9 * Dispatches a Command object to registered "<I>executants</I>" able to handle the command;
10 * each command execution may be handled by a different Thread so every registered agent does not have to wait
11 * for termination of code from the previous call.
12 * <P>
13 * <B>BEWARE</B> :
14 * <UL>
15 * <LI>
16 * The default constructor implementation uses a "cached" Thread Pool :
17 * <UL>
18 * <LI>
19 * It is not guaranteed that events will be really executed in parallel.
20 * A fixed Thread pool knowing about the number of processors or a pure sequential code may be
21 * more efficient: test! (and ask for changes!)
22 * <BR>
23 * Better use this class if the Command's code takes quite a lot of time to execute.
24 * <LI>
25 * The Thread pool may "choke" if commands to be executed are registered at a higher rate than
26 * they can be executed.
27 * </UL>
28 * <LI>
29 * The Threads handling the commands are deamon Threads. This may be a problem if you need an application Thread ...
30 * Moreover it is not guaranteed that all commands will be executed if the JVM abruptly terminates.
31 * <BR>
32 * This can be changed .... just ask!
33 *
34 * </UL>
35 *
36 *
37 * @param <T> the nature of the code able to handle the <TT>CommandFor<T></TT> Objects
38 * @author bernard AMADE
39 */
40 public class ParallelCommandDispatcher<T> {
41
42 /**
43 * Object of this embedded class are for registering a task to be handled by the executing Threads.
44 * @param <T2> code able to handle the corresponding <TT>CommandFor</TT> Objects.
45 */
46 static class DoIt<T2> implements Runnable {
47 public final T2 executant ;
48 public final CommandFor<T2> command ;
49
50 DoIt(T2 executant, CommandFor<T2> command) {
51 //System.out.println("#### command submitted : " + command);
52 this.executant = executant;
53 this.command = command;
54 }
55
56 @Override
57 public void run() {
58 try{
59 //System.out.println("#### command executed " + command );
60 command.invokeOn(executant);
61 } catch (Exception exc) {
62 PackCst.CURLOG.log(Level.SEVERE, "while executing :", exc);
63 }
64 }
65 }
66
67 /**
68 * ExecutorService that deals with the Threads handling the commands.
69 */
70 protected ExecutorService loop ;
71
72 /**
73 * List of all registered agent that may take action.
74 */
75 protected CopyOnWriteArrayList<T> list = new java.util.concurrent.CopyOnWriteArrayList<T>() ;
76
77
78 /**
79 * Creates an asynchronous Dispatcher for Commands.
80 * Uses the standard common ExecutorService for this package.
81 */
82 public ParallelCommandDispatcher() {
83 this.loop = PackCst.EXECUTOR_SERVICE ;
84 }
85
86 /**
87 * Creates an asynchronous Dispatcher for Commands: uses a specific ExecutorService.
88 */
89 public ParallelCommandDispatcher(ExecutorService execService) {
90 this.loop = execService ;
91 }
92
93 public ParallelCommandDispatcher(boolean singleThread) {
94 if(singleThread) {
95 this.loop = PackCst.SINGLE_THREAD ;
96 } else {
97 this.loop = PackCst.EXECUTOR_SERVICE ;
98 }
99 }
100 /**
101 * adds an executant to the list of those going to execute Commands when needed.
102 * @param executant
103 */
104 public void addExecutant(T executant) {
105 list.add(executant) ;
106 }
107
108 /**
109 * removes an executant form the list
110 *
111 * @param executant
112 */
113
114 public void removeExecutant(T executant) {
115 list.remove(executant) ;
116 }
117
118 /**
119 * Send a Command request for all registered executants.
120 * @param command
121 */
122 public void dispatchCommand(CommandFor<T> command) {
123 //System.out.println("# dispatching to " + list.size());
124 for(T executant : list) {
125 loop.submit(new DoIt<T>(executant, command)) ;
126 }
127 }
128 }