1 /*****************************************************/
\r
2 /* This java file is a part of the */
\r
4 /* - Plouf's Java IRC Client - */
\r
6 /* Copyright (C) 2002 - 2005 Philippe Detournay */
\r
8 /* All contacts : theplouf@yahoo.com */
\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
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
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
28 /*****************************************************/
\r
32 import java.lang.reflect.InvocationTargetException;
\r
33 import java.lang.reflect.Method;
\r
34 import java.util.Hashtable;
\r
43 public Object target;
\r
45 * Method name to call on target.
\r
47 public String method;
\r
49 * Method parameters.
\r
51 public Object[] params;
\r
53 * Lock that will be signaled upon completion.
\r
55 public Object endLock;
\r
57 * Method call result, if resultException is null.
\r
59 public Object result;
\r
61 * Method call exception, or null if all went well.
\r
63 public Throwable resultException;
\r
65 * True if call has completed.
\r
67 public boolean resultAvailable;
\r
70 * Create a new EventItem
\r
76 public EventItem(Object atarget, String amethod, Object[] aparams) {
\r
80 endLock = new Object();
\r
81 resultAvailable = false;
\r
90 class DispatchThread extends Thread {
\r
91 private irc.LinkedList _list;
\r
93 private Object _manageLock;
\r
94 private boolean _terminated;
\r
95 private boolean _processing;
\r
98 * Create a new DispatchThread
\r
101 * thread type : will be added in the name.
\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
114 * Add an event in the dispatch thread queue.
\r
119 * method name to call.
\r
122 * @return the newly created event item.
\r
124 public EventItem addEvent(Object target, String method, Object[] params) {
\r
127 EventItem item = new EventItem(target, method, params);
\r
128 synchronized (_manageLock) {
\r
129 _list.addLast(item);
\r
130 _manageLock.notify();
\r
136 public void run() {
\r
140 synchronized (_manageLock) {
\r
141 if (_list.size() > 0)
\r
142 item = (EventItem) _list.removeFirst();
\r
146 _manageLock.wait();
\r
147 } catch (InterruptedException ex) {
\r
151 size = _list.size();
\r
153 if (item != null) {
\r
154 _processing = true;
\r
155 item.resultException = null;
\r
157 item.result = EventDispatcher.dispatchEventSyncEx(item.target, item.method, item.params);
\r
158 } catch (Throwable e) {
\r
159 item.resultException = e;
\r
161 _processing = false;
\r
162 synchronized (item.endLock) {
\r
163 item.resultAvailable = true;
\r
164 item.endLock.notify();
\r
167 } while (!(_terminated && (size == 0)));
\r
171 * Terminate the event thread processing.
\r
173 public void terminate() {
\r
174 _terminated = true;
\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
187 public class EventDispatcher {
\r
188 private static final int USER = 0;
\r
189 private static final int SECURITY = 1;
\r
191 private static final String[] _names = { "User", "Security" };
\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
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
203 private static boolean match(Class[] t1, Class[] t2) {
\r
204 if (t1.length != t2.length)
\r
206 for (int i = 0; i < t1.length; i++) {
\r
208 if (!t1[i].isAssignableFrom(t2[i]))
\r
215 * Clear the internal EventDispatcher method cache.
\r
217 public static void clearCache() {
\r
218 synchronized (_cache) {
\r
224 * Disabe the synchroneous call thread check.
\r
226 public static void disableBadThreadWarning() {
\r
231 * Enable the synchroneous call thread check.
\r
233 public static void enableBadThreadWarning() {
\r
238 * Dispatch the given event in the current thread, ignoring any thrown
\r
244 * event method name.
\r
246 * event parameters.
\r
247 * @return the method result.
\r
248 * @deprecated Use dispatchEventSyncEx instead.
\r
251 public static Object dispatchEventSync(Object target, String method, Object[] params) {
\r
253 return dispatchEventSyncEx(target, method, params);
\r
254 } catch (Throwable ex) {
\r
255 ex.printStackTrace();
\r
261 * Dispatch the given event in the current thread, not ignoring any thrown
\r
267 * event method name.
\r
269 * event parameters.
\r
270 * @return the method result.
\r
271 * @throws Throwable
\r
273 public static Object dispatchEventSyncEx(Object target, String method, Object[] params) throws Throwable {
\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
285 Class c = target.getClass();
\r
288 synchronized (_cache) {
\r
289 m = (Method[]) _cache.get(c);
\r
291 m = c.getMethods();
\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
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
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
322 * Dispatch a new event to the given target in the event thread. The method
\r
323 * result is discarded.
\r
326 * target event listener.
\r
328 * method name to call.
\r
330 * parameters to pass to the called method.
\r
332 public static void dispatchEventAsync(Object target, String method, Object[] params) {
\r
334 _thread[USER].addEvent(target, method, params);
\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
342 private static void checkDeadLock(int index) {
\r
343 ensureAlive(index);
\r
344 if (Thread.currentThread() == _thread[index]) {
\r
346 throw new RuntimeException("Deadlock protection");
\r
347 } catch (RuntimeException ex) {
\r
348 ex.printStackTrace();
\r
355 * Dispatch a new event to the given target in the security event thread. The
\r
356 * method result is discarded.
\r
359 * target event listener.
\r
361 * method name to call.
\r
363 * parameters to pass to the called method.
\r
365 public static void dispatchEventAsyncSecurity(Object target, String method, Object[] params) {
\r
367 ensureAlive(SECURITY);
\r
368 _thread[SECURITY].addEvent(target, method, params);
\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
382 item.endLock.wait();
\r
383 if (item.resultException != null)
\r
384 throw item.resultException;
\r
385 return item.result;
\r
390 * Dispatch a new event in the event thread, waiting for the result and
\r
394 * target event listener.
\r
396 * method name to call.
\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
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
410 * Dispatch a new event in the security event thread, waiting for the result
\r
411 * and returning it.
\r
414 * target event listener.
\r
416 * method name to call.
\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
424 public static Object dispatchEventAsyncAndWaitExSecurity(Object target, String method, Object[] params)
\r
425 throws InterruptedException, Throwable {
\r
427 return dispatchEventAsyncAndWaitExImp(target, method, params, SECURITY);
\r
431 * Dispatch a new event in the event thread, waiting for the result and
\r
435 * target event listener.
\r
437 * method name to call.
\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
446 public static Object dispatchEventAsyncAndWait(Object target, String method, Object[] params)
\r
447 throws InterruptedException {
\r
448 checkDeadLock(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
455 item.endLock.wait();
\r
456 return item.result;
\r
461 * Check if the calling thread is the event thread.
\r
463 * @return true if calling thread is event thread, false otherwise.
\r
465 public static boolean isEventThread() {
\r
467 return Thread.currentThread() == _thread[USER] || Thread.currentThread() == _thread[SECURITY];
\r