KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > databinding > observable > Realm


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Brad Reynolds - bug 168153
11  *******************************************************************************/

12
13 package org.eclipse.core.databinding.observable;
14
15 import java.util.LinkedList JavaDoc;
16
17 import org.eclipse.core.databinding.Binding;
18 import org.eclipse.core.databinding.util.Policy;
19 import org.eclipse.core.runtime.ISafeRunnable;
20 import org.eclipse.core.runtime.IStatus;
21 import org.eclipse.core.runtime.SafeRunner;
22 import org.eclipse.core.runtime.Status;
23
24 /**
25  * A realm defines a context from which objects implementing {@link IObservable}
26  * must be accessed, and on which these objects will notify their listeners. To
27  * bridge between observables from different realms, subclasses of
28  * {@link Binding} can be used.
29  * <p>
30  * A block of code is said to be executing within a realm if calling
31  * {@link #isCurrent()} from that block returns true. Code reached by calling
32  * methods from that block will execute within the same realm, with the
33  * exception of methods on this class that can be used to execute code within a
34  * specific realm. Clients can use {@link #syncExec(Runnable)},
35  * {@link #asyncExec(Runnable)}, or {@link #exec(Runnable)} to execute a
36  * runnable within this realm. Note that using {@link #syncExec(Runnable)} can
37  * lead to deadlocks and should be avoided if the current thread holds any
38  * locks.
39  * </p>
40  * <p>
41  * It is instructive to think about possible implementations of Realm: It can be
42  * based on executing on a designated thread such as a UI thread, or based on
43  * holding a lock. In the former case, calling syncExec on a realm that is not
44  * the current realm will execute the given runnable on a different thread (the
45  * designated thread). In the latter case, calling syncExec may execute the
46  * given runnable on the calling thread, but calling
47  * {@link #asyncExec(Runnable)} will execute the given runnable on a different
48  * thread. Therefore, no assumptions can be made about the thread that will
49  * execute arguments to {@link #asyncExec(Runnable)},
50  * {@link #syncExec(Runnable)}, or {@link #exec(Runnable)}.
51  * </p>
52  * <p>
53  * It is possible that a block of code is executing within more than one realm.
54  * This can happen for implementations of Realm that are based on holding a lock
55  * but don't use a separate thread to run runnables given to
56  * {@link #syncExec(Runnable)}. Realm implementations of this kind should be
57  * appropriately documented because it increases the opportunity for deadlock.
58  * </p>
59  * <p>
60  * Some implementations of {@link IObservable} provide constructors which do not
61  * take a Realm argument and are specified to create the observable instance
62  * with the current default realm. The default realm can be set for the
63  * currently executing thread by using {@link #runWithDefault(Realm, Runnable)}.
64  * Note that the default realm does not have to be the current realm.
65  * </p>
66  * <p>
67  * Subclasses must override at least one of asyncExec()/syncExec(). For realms
68  * based on a designated thread, it may be easier to implement asyncExec and
69  * keep the default implementation of syncExec. For realms based on holding a
70  * lock, it may be easier to implement syncExec and keep the default
71  * implementation of asyncExec.
72  * </p>
73  *
74  * @since 1.0
75  *
76  * @see IObservable
77  */

78 public abstract class Realm {
79
80     private static ThreadLocal JavaDoc defaultRealm = new ThreadLocal JavaDoc();
81
82     /**
83      * Returns the default realm for the calling thread, or <code>null</code>
84      * if no default realm has been set.
85      *
86      * @return the default realm, or <code>null</code>
87      */

88     public static Realm getDefault() {
89         return (Realm) defaultRealm.get();
90     }
91     
92     /**
93      * Sets the default realm for the calling thread, returning the current
94      * default thread. This method is inherently unsafe, it is recommended to
95      * use {@link #runWithDefault(Realm, Runnable)} instead. This method is
96      * exposed to subclasses to facilitate testing.
97      *
98      * @param realm
99      * the new default realm, or <code>null</code>
100      * @return the previous default realm, or <code>null</code>
101      */

102     protected static Realm setDefault(Realm realm) {
103         Realm oldValue = getDefault();
104         defaultRealm.set(realm);
105         return oldValue;
106     }
107
108     /**
109      * @return true if the caller is executing in this realm. This method must
110      * not have side-effects (such as, for example, implicitly placing
111      * the caller in this realm).
112      */

113     abstract public boolean isCurrent();
114
115     private Thread JavaDoc workerThread;
116
117     LinkedList JavaDoc workQueue = new LinkedList JavaDoc();
118     
119     /**
120      * Runs the given runnable. If an exception occurs within the runnable, it
121      * is logged and not re-thrown. If the runnable implements
122      * {@link ISafeRunnable}, the exception is passed to its
123      * <code>handleException<code> method.
124      *
125      * @param runnable
126      */

127     protected static void safeRun(final Runnable JavaDoc runnable) {
128         ISafeRunnable safeRunnable;
129         if (runnable instanceof ISafeRunnable) {
130             safeRunnable = (ISafeRunnable) runnable;
131         } else {
132             safeRunnable = new ISafeRunnable() {
133                 public void handleException(Throwable JavaDoc exception) {
134                     Policy
135                             .getLog()
136                             .log(
137                                     new Status(
138                                             IStatus.ERROR,
139                                             Policy.JFACE_DATABINDING,
140                                             IStatus.OK,
141                                             "Unhandled exception: " + exception.getMessage(), exception)); //$NON-NLS-1$
142
}
143                 public void run() throws Exception JavaDoc {
144                     runnable.run();
145                 }
146             };
147         }
148         SafeRunner.run(safeRunnable);
149     }
150
151     /**
152      * Causes the <code>run()</code> method of the runnable to be invoked from
153      * within this realm. If the caller is executing in this realm, the
154      * runnable's run method is invoked directly, otherwise it is run at the
155      * next reasonable opportunity using asyncExec.
156      * <p>
157      * If the given runnable is an instance of {@link ISafeRunnable}, its
158      * exception handler method will be called if any exceptions occur while
159      * running it. Otherwise, the exception will be logged.
160      * </p>
161      *
162      * @param runnable
163      */

164     public void exec(Runnable JavaDoc runnable) {
165         if (isCurrent()) {
166             safeRun(runnable);
167         } else {
168             asyncExec(runnable);
169         }
170     }
171
172     /**
173      * Causes the <code>run()</code> method of the runnable to be invoked from
174      * within this realm at the next reasonable opportunity. The caller of this
175      * method continues to run in parallel, and is not notified when the
176      * runnable has completed.
177      * <p>
178      * If the given runnable is an instance of {@link ISafeRunnable}, its
179      * exception handler method will be called if any exceptions occur while
180      * running it. Otherwise, the exception will be logged.
181      * </p>
182      * <p>
183      * Subclasses should use {@link #safeRun(Runnable)} to run the runnable.
184      * </p>
185      *
186      * @param runnable
187      */

188     public void asyncExec(Runnable JavaDoc runnable) {
189         synchronized (workQueue) {
190             ensureWorkerThreadIsRunning();
191             workQueue.addLast(runnable);
192             workQueue.notifyAll();
193         }
194     }
195
196     /**
197      *
198      */

199     private void ensureWorkerThreadIsRunning() {
200         if (workerThread == null) {
201             workerThread = new Thread JavaDoc() {
202                 public void run() {
203                     try {
204                         while (true) {
205                             Runnable JavaDoc work = null;
206                             synchronized (workQueue) {
207                                 while (workQueue.isEmpty()) {
208                                     workQueue.wait();
209                                 }
210                                 work = (Runnable JavaDoc) workQueue.removeFirst();
211                             }
212                             syncExec(work);
213                         }
214                     } catch (InterruptedException JavaDoc e) {
215                         // exit
216
}
217                 }
218             };
219             workerThread.start();
220         }
221     }
222
223     /**
224      * Causes the <code>run()</code> method of the runnable to be invoked from
225      * within this realm at the next reasonable opportunity. This method is
226      * blocking the caller until the runnable completes.
227      * <p>
228      * If the given runnable is an instance of {@link ISafeRunnable}, its
229      * exception handler method will be called if any exceptions occur while
230      * running it. Otherwise, the exception will be logged.
231      * </p>
232      * <p>
233      * Subclasses should use {@link #safeRun(Runnable)} to run the runnable.
234      * </p>
235      * <p>
236      * Note: This class is not meant to be called by clients and therefore has
237      * only protected access.
238      * </p>
239      *
240      * @param runnable
241      */

242     protected void syncExec(Runnable JavaDoc runnable) {
243         SyncRunnable syncRunnable = new SyncRunnable(runnable);
244         asyncExec(syncRunnable);
245         synchronized (syncRunnable) {
246             while (!syncRunnable.hasRun) {
247                 try {
248                     syncRunnable.wait();
249                 } catch (InterruptedException JavaDoc e) {
250                     Thread.currentThread().interrupt();
251                 }
252             }
253         }
254     }
255
256     static class SyncRunnable implements Runnable JavaDoc {
257         boolean hasRun = false;
258
259         private Runnable JavaDoc runnable;
260
261         SyncRunnable(Runnable JavaDoc runnable) {
262             this.runnable = runnable;
263         }
264
265         public void run() {
266             try {
267                 safeRun(runnable);
268             } finally {
269                 synchronized (this) {
270                     hasRun = true;
271                     this.notifyAll();
272                 }
273             }
274         }
275     }
276
277     /**
278      * Sets the provided <code>realm</code> as the default for the duration of
279      * {@link Runnable#run()} and resets the previous realm after completion.
280      * Note that this will not set the given realm as the current realm.
281      *
282      * @param realm
283      * @param runnable
284      */

285     public static void runWithDefault(Realm realm, Runnable JavaDoc runnable) {
286         Realm oldRealm = Realm.getDefault();
287         try {
288             defaultRealm.set(realm);
289             runnable.run();
290         } finally {
291             defaultRealm.set(oldRealm);
292         }
293     }
294 }
295
Popular Tags