]> git.somenet.org - irc/pjirc-ng.git/blob - src/main/java/irc/EventDispatcher.java
Pjirc 2.2.1 as available on the net, reformatted and made it compile.
[irc/pjirc-ng.git] / src / main / java / irc / EventDispatcher.java
1 /*****************************************************/\r
2 /*          This java file is a part of the          */\r
3 /*                                                   */\r
4 /*           -  Plouf's Java IRC Client  -           */\r
5 /*                                                   */\r
6 /*   Copyright (C)  2002 - 2005 Philippe Detournay   */\r
7 /*                                                   */\r
8 /*         All contacts : theplouf@yahoo.com         */\r
9 /*                                                   */\r
10 /*  PJIRC is free software; you can redistribute     */\r
11 /*  it and/or modify it under the terms of the GNU   */\r
12 /*  General Public License as published by the       */\r
13 /*  Free Software Foundation; version 2 or later of  */\r
14 /*  the License.                                     */\r
15 /*                                                   */\r
16 /*  PJIRC is distributed in the hope that it will    */\r
17 /*  be useful, but WITHOUT ANY WARRANTY; without     */\r
18 /*  even the implied warranty of MERCHANTABILITY or  */\r
19 /*  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   */\r
20 /*  General Public License for more details.         */\r
21 /*                                                   */\r
22 /*  You should have received a copy of the GNU       */\r
23 /*  General Public License along with PJIRC; if      */\r
24 /*  not, write to the Free Software Foundation,      */\r
25 /*  Inc., 59 Temple Place, Suite 330, Boston,        */\r
26 /*  MA  02111-1307  USA                              */\r
27 /*                                                   */\r
28 /*****************************************************/\r
29 \r
30 package irc;\r
31 \r
32 import java.lang.reflect.InvocationTargetException;\r
33 import java.lang.reflect.Method;\r
34 import java.util.Hashtable;\r
35 \r
36 /**\r
37  * EventItem.\r
38  */\r
39 class EventItem {\r
40         /**\r
41          * Target object.\r
42          */\r
43         public Object target;\r
44         /**\r
45          * Method name to call on target.\r
46          */\r
47         public String method;\r
48         /**\r
49          * Method parameters.\r
50          */\r
51         public Object[] params;\r
52         /**\r
53          * Lock that will be signaled upon completion.\r
54          */\r
55         public Object endLock;\r
56         /**\r
57          * Method call result, if resultException is null.\r
58          */\r
59         public Object result;\r
60         /**\r
61          * Method call exception, or null if all went well.\r
62          */\r
63         public Throwable resultException;\r
64         /**\r
65          * True if call has completed.\r
66          */\r
67         public boolean resultAvailable;\r
68 \r
69         /**\r
70          * Create a new EventItem\r
71          * \r
72          * @param atarget\r
73          * @param amethod\r
74          * @param aparams\r
75          */\r
76         public EventItem(Object atarget, String amethod, Object[] aparams) {\r
77                 target = atarget;\r
78                 method = amethod;\r
79                 params = aparams;\r
80                 endLock = new Object();\r
81                 resultAvailable = false;\r
82                 result = null;\r
83         }\r
84 \r
85 }\r
86 \r
87 /**\r
88  * DispatchThread.\r
89  */\r
90 class DispatchThread extends Thread {\r
91         private irc.LinkedList _list;\r
92 \r
93         private Object _manageLock;\r
94         private boolean _terminated;\r
95         private boolean _processing;\r
96 \r
97         /**\r
98          * Create a new DispatchThread\r
99          * \r
100          * @param type\r
101          *          thread type : will be added in the name.\r
102          */\r
103         public DispatchThread(String type) {\r
104                 super(type + " event dispatch thread");\r
105                 _manageLock = new Object();\r
106                 _list = new LinkedList();\r
107                 _terminated = false;\r
108                 _processing = false;\r
109                 setDaemon(true);\r
110                 start();\r
111         }\r
112 \r
113         /**\r
114          * Add an event in the dispatch thread queue.\r
115          * \r
116          * @param target\r
117          *          target object.\r
118          * @param method\r
119          *          method name to call.\r
120          * @param params\r
121          *          call parameters.\r
122          * @return the newly created event item.\r
123          */\r
124         public EventItem addEvent(Object target, String method, Object[] params) {\r
125                 if (_terminated)\r
126                         return null;\r
127                 EventItem item = new EventItem(target, method, params);\r
128                 synchronized (_manageLock) {\r
129                         _list.addLast(item);\r
130                         _manageLock.notify();\r
131                 }\r
132                 return item;\r
133         }\r
134 \r
135         @Override\r
136         public void run() {\r
137                 int size = 0;\r
138                 do {\r
139                         EventItem item;\r
140                         synchronized (_manageLock) {\r
141                                 if (_list.size() > 0)\r
142                                         item = (EventItem) _list.removeFirst();\r
143                                 else {\r
144                                         item = null;\r
145                                         try {\r
146                                                 _manageLock.wait();\r
147                                         } catch (InterruptedException ex) {\r
148                                                 // ignore...\r
149                                         }\r
150                                 }\r
151                                 size = _list.size();\r
152                         }\r
153                         if (item != null) {\r
154                                 _processing = true;\r
155                                 item.resultException = null;\r
156                                 try {\r
157                                         item.result = EventDispatcher.dispatchEventSyncEx(item.target, item.method, item.params);\r
158                                 } catch (Throwable e) {\r
159                                         item.resultException = e;\r
160                                 }\r
161                                 _processing = false;\r
162                                 synchronized (item.endLock) {\r
163                                         item.resultAvailable = true;\r
164                                         item.endLock.notify();\r
165                                 }\r
166                         }\r
167                 } while (!(_terminated && (size == 0)));\r
168         }\r
169 \r
170         /**\r
171          * Terminate the event thread processing.\r
172          */\r
173         public void terminate() {\r
174                 _terminated = true;\r
175                 if (!_processing)\r
176                         interrupt();\r
177         }\r
178 }\r
179 \r
180 /**\r
181  * Event dispatcher, using reflection and PJIRC threading model. The PJIRC\r
182  * threading model states that, unless specified otherwise, any call to any\r
183  * method of any object should be performed in the event dispatcher thread. This\r
184  * can be ensured via isEventThread. Any call to dispatchEventSync in an other\r
185  * thread will lead to an error message displayed on the error output.\r
186  */\r
187 public class EventDispatcher {\r
188         private static final int USER = 0;\r
189         private static final int SECURITY = 1;\r
190 \r
191         private static final String[] _names = { "User", "Security" };\r
192 \r
193         private static Hashtable _cache = new Hashtable();\r
194         private static DispatchThread[] _thread = new DispatchThread[2];\r
195         private static boolean _warning = true;\r
196 \r
197         private static void ensureAlive(int index) {\r
198                 if ((_thread[index] == null) || (!_thread[index].isAlive())) {\r
199                         _thread[index] = new DispatchThread(_names[index]);\r
200                 }\r
201         }\r
202 \r
203         private static boolean match(Class[] t1, Class[] t2) {\r
204                 if (t1.length != t2.length)\r
205                         return false;\r
206                 for (int i = 0; i < t1.length; i++) {\r
207                         if (t2[i] != null)\r
208                                 if (!t1[i].isAssignableFrom(t2[i]))\r
209                                         return false;\r
210                 }\r
211                 return true;\r
212         }\r
213 \r
214         /**\r
215          * Clear the internal EventDispatcher method cache.\r
216          */\r
217         public static void clearCache() {\r
218                 synchronized (_cache) {\r
219                         _cache.clear();\r
220                 }\r
221         }\r
222 \r
223         /**\r
224          * Disabe the synchroneous call thread check.\r
225          */\r
226         public static void disableBadThreadWarning() {\r
227                 _warning = false;\r
228         }\r
229 \r
230         /**\r
231          * Enable the synchroneous call thread check.\r
232          */\r
233         public static void enableBadThreadWarning() {\r
234                 _warning = true;\r
235         }\r
236 \r
237         /**\r
238          * Dispatch the given event in the current thread, ignoring any thrown\r
239          * exception.\r
240          * \r
241          * @param target\r
242          *          event target.\r
243          * @param method\r
244          *          event method name.\r
245          * @param params\r
246          *          event parameters.\r
247          * @return the method result.\r
248          * @deprecated Use dispatchEventSyncEx instead.\r
249          */\r
250         @Deprecated\r
251         public static Object dispatchEventSync(Object target, String method, Object[] params) {\r
252                 try {\r
253                         return dispatchEventSyncEx(target, method, params);\r
254                 } catch (Throwable ex) {\r
255                         ex.printStackTrace();\r
256                         return null;\r
257                 }\r
258         }\r
259 \r
260         /**\r
261          * Dispatch the given event in the current thread, not ignoring any thrown\r
262          * exception.\r
263          * \r
264          * @param target\r
265          *          event target.\r
266          * @param method\r
267          *          event method name.\r
268          * @param params\r
269          *          event parameters.\r
270          * @return the method result.\r
271          * @throws Throwable\r
272          */\r
273         public static Object dispatchEventSyncEx(Object target, String method, Object[] params) throws Throwable {\r
274                 ensureAlive(USER);\r
275 \r
276                 if (_warning && !isEventThread()) {\r
277                         System.err.println("Event dispatch in wrong thread");\r
278                         System.err.println("expected thread was " + _thread);\r
279                         System.err.println("current thread is " + Thread.currentThread());\r
280                         System.err.println("please submit a bug report to plouf@pjirc.com with the following information :");\r
281                         Thread.dumpStack();\r
282                 }\r
283 \r
284                 try {\r
285                         Class c = target.getClass();\r
286 \r
287                         Method m[];\r
288                         synchronized (_cache) {\r
289                                 m = (Method[]) _cache.get(c);\r
290                                 if (m == null) {\r
291                                         m = c.getMethods();\r
292                                         _cache.put(c, m);\r
293                                 }\r
294                         }\r
295 \r
296                         Class types[] = new Class[params.length];\r
297                         for (int i = 0; i < params.length; i++) {\r
298                                 if (params[i] != null)\r
299                                         types[i] = params[i].getClass();\r
300                                 else\r
301                                         types[i] = null;\r
302                         }\r
303                         for (int i = 0; i < m.length; i++) {\r
304                                 if (m[i].getName().equals(method)) {\r
305                                         if (match(m[i].getParameterTypes(), types)) {\r
306                                                 return m[i].invoke(target, params);\r
307                                         }\r
308                                 }\r
309                         }\r
310                         throw new NoSuchMethodException(method);\r
311                 } catch (InvocationTargetException ex) {\r
312                         throw ex.getTargetException();\r
313                 } catch (Throwable ex) {\r
314                         System.err.println("internal error");\r
315                         System.err.println("please submit a bug report to plouf@pjirc.com with the following information :");\r
316                         ex.printStackTrace();\r
317                         return null;\r
318                 }\r
319         }\r
320 \r
321         /**\r
322          * Dispatch a new event to the given target in the event thread. The method\r
323          * result is discarded.\r
324          * \r
325          * @param target\r
326          *          target event listener.\r
327          * @param method\r
328          *          method name to call.\r
329          * @param params\r
330          *          parameters to pass to the called method.\r
331          */\r
332         public static void dispatchEventAsync(Object target, String method, Object[] params) {\r
333                 ensureAlive(USER);\r
334                 _thread[USER].addEvent(target, method, params);\r
335         }\r
336 \r
337         private static void checkStack() {\r
338                 // we want to avoid code other that irc.security.* calls this method\r
339                 // unable to implement on 1.1...\r
340         }\r
341 \r
342         private static void checkDeadLock(int index) {\r
343                 ensureAlive(index);\r
344                 if (Thread.currentThread() == _thread[index]) {\r
345                         try {\r
346                                 throw new RuntimeException("Deadlock protection");\r
347                         } catch (RuntimeException ex) {\r
348                                 ex.printStackTrace();\r
349                                 throw ex;\r
350                         }\r
351                 }\r
352         }\r
353 \r
354         /**\r
355          * Dispatch a new event to the given target in the security event thread. The\r
356          * method result is discarded.\r
357          * \r
358          * @param target\r
359          *          target event listener.\r
360          * @param method\r
361          *          method name to call.\r
362          * @param params\r
363          *          parameters to pass to the called method.\r
364          */\r
365         public static void dispatchEventAsyncSecurity(Object target, String method, Object[] params) {\r
366                 checkStack();\r
367                 ensureAlive(SECURITY);\r
368                 _thread[SECURITY].addEvent(target, method, params);\r
369         }\r
370 \r
371         private static Object dispatchEventAsyncAndWaitExImp(Object target, String method, Object[] params, int index)\r
372                         throws InterruptedException, Throwable {\r
373                 checkDeadLock(index);\r
374                 ensureAlive(index);\r
375                 EventItem item = _thread[index].addEvent(target, method, params);\r
376                 synchronized (item.endLock) {\r
377                         if (item.resultAvailable) {\r
378                                 if (item.resultException != null)\r
379                                         throw item.resultException;\r
380                                 return item.result;\r
381                         }\r
382                         item.endLock.wait();\r
383                         if (item.resultException != null)\r
384                                 throw item.resultException;\r
385                         return item.result;\r
386                 }\r
387         }\r
388 \r
389         /**\r
390          * Dispatch a new event in the event thread, waiting for the result and\r
391          * returning it.\r
392          * \r
393          * @param target\r
394          *          target event listener.\r
395          * @param method\r
396          *          method name to call.\r
397          * @param params\r
398          *          parameters to pass to the called method.\r
399          * @return method result.\r
400          * @throws InterruptedException\r
401          *           if the wait is interrupted.\r
402          * @throws Throwable\r
403          */\r
404         public static Object dispatchEventAsyncAndWaitEx(Object target, String method, Object[] params)\r
405                         throws InterruptedException, Throwable {\r
406                 return dispatchEventAsyncAndWaitExImp(target, method, params, USER);\r
407         }\r
408 \r
409         /**\r
410          * Dispatch a new event in the security event thread, waiting for the result\r
411          * and returning it.\r
412          * \r
413          * @param target\r
414          *          target event listener.\r
415          * @param method\r
416          *          method name to call.\r
417          * @param params\r
418          *          parameters to pass to the called method.\r
419          * @return method result.\r
420          * @throws InterruptedException\r
421          *           if the wait is interrupted.\r
422          * @throws Throwable\r
423          */\r
424         public static Object dispatchEventAsyncAndWaitExSecurity(Object target, String method, Object[] params)\r
425                         throws InterruptedException, Throwable {\r
426                 checkStack();\r
427                 return dispatchEventAsyncAndWaitExImp(target, method, params, SECURITY);\r
428         }\r
429 \r
430         /**\r
431          * Dispatch a new event in the event thread, waiting for the result and\r
432          * returning it.\r
433          * \r
434          * @param target\r
435          *          target event listener.\r
436          * @param method\r
437          *          method name to call.\r
438          * @param params\r
439          *          parameters to pass to the called method.\r
440          * @return method result.\r
441          * @throws InterruptedException\r
442          *           if the wait is interrupted.\r
443          * @deprecated Use dispatchEventAsyncAndWaitEx instead.\r
444          */\r
445         @Deprecated\r
446         public static Object dispatchEventAsyncAndWait(Object target, String method, Object[] params)\r
447                         throws InterruptedException {\r
448                 checkDeadLock(USER);\r
449                 ensureAlive(USER);\r
450                 EventItem item = _thread[USER].addEvent(target, method, params);\r
451                 synchronized (item.endLock) {\r
452                         if (item.resultAvailable) {\r
453                                 return item.result;\r
454                         }\r
455                         item.endLock.wait();\r
456                         return item.result;\r
457                 }\r
458         }\r
459 \r
460         /**\r
461          * Check if the calling thread is the event thread.\r
462          * \r
463          * @return true if calling thread is event thread, false otherwise.\r
464          */\r
465         public static boolean isEventThread() {\r
466                 ensureAlive(USER);\r
467                 return Thread.currentThread() == _thread[USER] || Thread.currentThread() == _thread[SECURITY];\r
468         }\r
469 }\r