KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > snmp > ThreadContext


1 /*
2  * @(#)file ThreadContext.java
3  * @(#)author Sun Microsystems, Inc.
4  * @(#)version 1.10
5  * @(#)date 08/02/09
6  *
7  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
8  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
9  *
10  */

11
12
13 package com.sun.jmx.snmp;
14
15 import java.util.Stack JavaDoc;
16 import java.util.EmptyStackException JavaDoc;
17
18 /**
19  * <p><b>Warning: The interface of this class is subject to change.
20  * Use at your own risk.</b></p>
21  *
22  * <p>This class associates a context with each thread that
23  * references it. The context is a set of mappings between Strings
24  * and Objects. It is managed as a stack, typically with code like
25  * this:</p>
26  *
27  * <pre>
28  * ThreadContext oldContext = ThreadContext.push(myKey, myObject);
29  * // plus possibly further calls to ThreadContext.push...
30  * try {
31  * doSomeOperation();
32  * } finally {
33  * ThreadContext.restore(oldContext);
34  * }
35  * </pre>
36  *
37  * <p>The <code>try</code>...<code>finally</code> block ensures that
38  * the <code>restore</code> is done even if
39  * <code>doSomeOperation</code> terminates abnormally (with an
40  * exception).</p>
41  *
42  * <p>A thread can consult its own context using
43  * <code>ThreadContext.get(myKey)</code>. The result is the
44  * value that was most recently pushed with the given key.</p>
45  *
46  * <p>A thread cannot read or modify the context of another thread.</p>
47  *
48  * <p><b>This API is a Sun Microsystems internal API and is subject
49  * to change without notice.</b></p>
50  * @version 1.10 12/19/03
51  * @author Sun Microsystems, Inc
52  */

53 public class ThreadContext implements Cloneable JavaDoc {
54
55     /* The context of a thread is stored as a linked list. At the
56        head of the list is the value returned by localContext.get().
57        At the tail of the list is a sentinel ThreadContext value with
58        "previous" and "key" both null. There is a different sentinel
59        object for each thread.
60
61        Because a null key indicates the sentinel, we reject attempts to
62        push context entries with a null key.
63        
64        The reason for using a sentinel rather than just terminating
65        the list with a null reference is to protect against incorrect
66        or even malicious code. If you have a reference to the
67        sentinel value, you can erase the context stack. Only the
68        caller of the first "push" that put something on the stack can
69        get such a reference, so if that caller does not give this
70        reference away, no one else can erase the stack.
71
72        If the restore method took a null reference to mean an empty
73        stack, anyone could erase the stack, since anyone can make a
74        null reference.
75     
76        When the stack is empty, we discard the sentinel object and
77        have localContext.get() return null. Then we recreate the
78        sentinel object on the first subsequent push.
79
80        ThreadContext objects are immutable. As a consequence, you can
81        give a ThreadContext object to setInitialContext that is no
82        longer current. But the interface says this can be rejected,
83        in case we remove immutability later. */

84
85     /* We have to comment out "final" here because of a bug in the JDK1.1
86        compiler. Uncomment it when we discard 1.1 compatibility. */

87     private /*final*/ ThreadContext previous;
88     private /*final*/ String JavaDoc key;
89     private /*final*/ Object JavaDoc value;
90
91     private ThreadContext(ThreadContext previous, String JavaDoc key, Object JavaDoc value) {
92     this.previous = previous;
93     this.key = key;
94     this.value = value;
95     }
96
97     /**
98      * <p>Get the Object that was most recently pushed with the given key.</p>
99      *
100      * @param key the key of interest.
101      *
102      * @return the last Object that was pushed (using
103      * <code>push</code>) with that key and not subsequently cancelled
104      * by a <code>restore</code>; or null if there is no such object.
105      * A null return value may also indicate that the last Object
106      * pushed was the value <code>null</code>. Use the
107      * <code>contains</code> method to distinguish this case from the
108      * case where there is no Object.
109      *
110      * @exception IllegalArgumentException if <code>key</code> is null.
111      */

112     public static Object JavaDoc get(String JavaDoc key) throws IllegalArgumentException JavaDoc {
113     ThreadContext context = contextContaining(key);
114     if (context == null)
115         return null;
116     else
117         return context.value;
118     }
119
120     /**
121      * <p>Check whether a value with the given key exists in the stack.
122      * This means that the <code>push</code> method was called with
123      * this key and it was not cancelled by a subsequent
124      * <code>restore</code>. This method is useful when the
125      * <code>get</code> method returns null, to distinguish between
126      * the case where the key exists in the stack but is associated
127      * with a null value, and the case where the key does not exist in
128      * the stack.</p>
129      *
130      * @return true if the key exists in the stack.
131      *
132      * @exception IllegalArgumentException if <code>key</code> is null.
133      */

134     public static boolean contains(String JavaDoc key)
135         throws IllegalArgumentException JavaDoc {
136     return (contextContaining(key) != null);
137     }
138
139     /**
140      * <p>Find the ThreadContext in the stack that contains the given key,
141      * or return null if there is none.</p>
142      *
143      * @exception IllegalArgumentException if <code>key</code> is null.
144      */

145     private static ThreadContext contextContaining(String JavaDoc key)
146         throws IllegalArgumentException JavaDoc {
147     if (key == null)
148         throw new IllegalArgumentException JavaDoc("null key");
149     for (ThreadContext context = getContext();
150          context != null;
151          context = context.previous) {
152         if (key.equals(context.key))
153         return context;
154         /* Note that "context.key" may be null if "context" is the
155            sentinel, so don't write "if (context.key.equals(key))"! */

156     }
157     return null;
158     }
159
160 // /**
161
// * Change the value that was most recently associated with the given key
162
// * in a <code>push</code> operation not cancelled by a subsequent
163
// * <code>restore</code>. If there is no such association, nothing happens
164
// * and the return value is null.
165
// *
166
// * @param key the key of interest.
167
// * @param value the new value to associate with that key.
168
// *
169
// * @return the value that was previously associated with the key, or null
170
// * if the key does not exist in the stack.
171
// *
172
// * @exception IllegalArgumentException if <code>key</code> is null.
173
// */
174
// public static Object set(String key, Object value)
175
// throws IllegalArgumentException {
176
// ThreadContext context = contextContaining(key);
177
// if (context == null)
178
// return null;
179
// Object old = context.value;
180
// context.value = value;
181
// return old;
182
// }
183

184     /**
185      * <p>Push an object on the context stack with the given key.
186      * This operation can subsequently be undone by calling
187      * <code>restore</code> with the ThreadContext value returned
188      * here.</p>
189      *
190      * @param key the key that will be used to find the object while it is
191      * on the stack.
192      * @param value the value to be associated with that key. It may be null.
193      *
194      * @return a ThreadContext that can be given to <code>restore</code> to
195      * restore the stack to its state before the <code>push</code>.
196      *
197      * @exception IllegalArgumentException if <code>key</code> is null.
198      */

199     public static ThreadContext push(String JavaDoc key, Object JavaDoc value)
200         throws IllegalArgumentException JavaDoc {
201     if (key == null)
202         throw new IllegalArgumentException JavaDoc("null key");
203
204     ThreadContext oldContext = getContext();
205     if (oldContext == null)
206         oldContext = new ThreadContext(null, null, null); // make sentinel
207
ThreadContext newContext = new ThreadContext(oldContext, key, value);
208     setContext(newContext);
209     return oldContext;
210     }
211
212     /**
213      * <p>Return an object that can later be supplied to <code>restore</code>
214      * to restore the context stack to its current state. The object can
215      * also be given to <code>setInitialContext</code>.</p>
216      *
217      * @return a ThreadContext that represents the current context stack.
218      */

219     public static ThreadContext getThreadContext() {
220     return getContext();
221     }
222
223     /**
224      * <p>Restore the context stack to an earlier state. This typically
225      * undoes the effect of one or more <code>push</code> calls.</p>
226      *
227      * @param oldContext the state to return. This is usually the return
228      * value of an earlier <code>push</code> operation.
229      *
230      * @exception NullPointerException if <code>oldContext</code> is null.
231      * @exception IllegalArgumentException if <code>oldContext</code>
232      * does not represent a context from this thread, or if that
233      * context was undone by an earlier <code>restore</code>.
234      */

235     public static void restore(ThreadContext oldContext)
236         throws NullPointerException JavaDoc, IllegalArgumentException JavaDoc {
237     /* The following test is not strictly necessary in the code as it
238        stands today, since the reference to "oldContext.key" would
239        generate a NullPointerException anyway. But if someone
240        didn't notice that during subsequent changes, they could
241        accidentally permit restore(null) with the semantics of
242        trashing the context stack. */

243     if (oldContext == null)
244         throw new NullPointerException JavaDoc();
245
246     /* Check that the restored context is in the stack. */
247     for (ThreadContext context = getContext();
248          context != oldContext;
249          context = context.previous) {
250         if (context == null) {
251         throw new IllegalArgumentException JavaDoc("Restored context is not " +
252                            "contained in current " +
253                            "context");
254         }
255     }
256
257     /* Discard the sentinel if the stack is empty. This means that it
258        is an error to call "restore" a second time with the
259        ThreadContext value that means an empty stack. That's why we
260        don't say that it is all right to restore the stack to the
261        state it was already in. */

262     if (oldContext.key == null)
263         oldContext = null;
264
265     setContext(oldContext);
266     }
267
268     /**
269      * <p>Set the initial context of the calling thread to a context obtained
270      * from another thread. After this call, the calling thread will see
271      * the same results from the <code>get</code> method as the thread
272      * from which the <code>context</code> argument was obtained, at the
273      * time it was obtained.</p>
274      *
275      * <p>The <code>context</code> argument must be the result of an earlier
276      * <code>push</code> or <code>getThreadContext</code> call. It is an
277      * error (which may or may not be detected) if this context has been
278      * undone by a <code>restore</code>.</p>
279      *
280      * <p>The context stack of the calling thread must be empty before this
281      * call, i.e., there must not have been a <code>push</code> not undone
282      * by a subsequent <code>restore</code>.</p>
283      *
284      * @exception IllegalArgumentException if the context stack was
285      * not empty before the call. An implementation may also throw this
286      * exception if <code>context</code> is no longer current in the
287      * thread from which it was obtained.
288      */

289     /* We rely on the fact that ThreadContext objects are immutable.
290        This means that we don't have to check that the "context"
291        argument is valid. It necessarily represents the head of a
292        valid chain of ThreadContext objects, even if the thread from
293        which it was obtained has subsequently been set to a point
294        later in that chain using "restore". */

295     public void setInitialContext(ThreadContext context)
296         throws IllegalArgumentException JavaDoc {
297     /* The following test assumes that we discard sentinels when the
298        stack is empty. */

299     if (getContext() != null)
300         throw new IllegalArgumentException JavaDoc("previous context not empty");
301     setContext(context);
302     }
303
304     private static ThreadContext getContext() {
305     return (ThreadContext) localContext.get();
306     }
307
308     private static void setContext(ThreadContext context) {
309     localContext.set(context);
310     }
311
312     private static ThreadLocal JavaDoc localContext = new ThreadLocal JavaDoc();
313 }
314
Popular Tags