1 /*
   2  * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package com.sun.jmx.snmp;
  28 
  29 import java.util.Stack;
  30 import java.util.EmptyStackException;
  31 
  32 /**
  33  * <p><b>Warning: The interface of this class is subject to change.
  34  * Use at your own risk.</b></p>
  35  *
  36  * <p>This class associates a context with each thread that
  37  * references it.  The context is a set of mappings between Strings
  38  * and Objects.  It is managed as a stack, typically with code like
  39  * this:</p>
  40  *
  41  * <pre>
  42  * ThreadContext oldContext = ThreadContext.push(myKey, myObject);
  43  * // plus possibly further calls to ThreadContext.push...
  44  * try {
  45  *      doSomeOperation();
  46  * } finally {
  47  *      ThreadContext.restore(oldContext);
  48  * }
  49  * </pre>
  50  *
  51  * <p>The <code>try</code>...<code>finally</code> block ensures that
  52  * the <code>restore</code> is done even if
  53  * <code>doSomeOperation</code> terminates abnormally (with an
  54  * exception).</p>
  55  *
  56  * <p>A thread can consult its own context using
  57  * <code>ThreadContext.get(myKey)</code>.  The result is the
  58  * value that was most recently pushed with the given key.</p>
  59  *
  60  * <p>A thread cannot read or modify the context of another thread.</p>
  61  *
  62  * <p><b>This API is a Sun Microsystems internal API  and is subject
  63  * to change without notice.</b></p>
  64  */
  65 public class ThreadContext implements Cloneable {
  66 
  67     /* The context of a thread is stored as a linked list.  At the
  68        head of the list is the value returned by localContext.get().
  69        At the tail of the list is a sentinel ThreadContext value with
  70        "previous" and "key" both null.  There is a different sentinel
  71        object for each thread.
  72 
  73        Because a null key indicates the sentinel, we reject attempts to
  74        push context entries with a null key.
  75 
  76        The reason for using a sentinel rather than just terminating
  77        the list with a null reference is to protect against incorrect
  78        or even malicious code.  If you have a reference to the
  79        sentinel value, you can erase the context stack.  Only the
  80        caller of the first "push" that put something on the stack can
  81        get such a reference, so if that caller does not give this
  82        reference away, no one else can erase the stack.
  83 
  84        If the restore method took a null reference to mean an empty
  85        stack, anyone could erase the stack, since anyone can make a
  86        null reference.
  87 
  88        When the stack is empty, we discard the sentinel object and
  89        have localContext.get() return null.  Then we recreate the
  90        sentinel object on the first subsequent push.
  91 
  92        ThreadContext objects are immutable.  As a consequence, you can
  93        give a ThreadContext object to setInitialContext that is no
  94        longer current.  But the interface says this can be rejected,
  95        in case we remove immutability later.  */
  96 
  97     /* We have to comment out "final" here because of a bug in the JDK1.1
  98        compiler.  Uncomment it when we discard 1.1 compatibility.  */
  99     private /*final*/ ThreadContext previous;
 100     private /*final*/ String key;
 101     private /*final*/ Object value;
 102 
 103     private ThreadContext(ThreadContext previous, String key, Object value) {
 104         this.previous = previous;
 105         this.key = key;
 106         this.value = value;
 107     }
 108 
 109     /**
 110      * <p>Get the Object that was most recently pushed with the given key.</p>
 111      *
 112      * @param key the key of interest.
 113      *
 114      * @return the last Object that was pushed (using
 115      * <code>push</code>) with that key and not subsequently canceled
 116      * by a <code>restore</code>; or null if there is no such object.
 117      * A null return value may also indicate that the last Object
 118      * pushed was the value <code>null</code>.  Use the
 119      * <code>contains</code> method to distinguish this case from the
 120      * case where there is no Object.
 121      *
 122      * @exception IllegalArgumentException if <code>key</code> is null.
 123      */
 124     public static Object get(String key) throws IllegalArgumentException {
 125         ThreadContext context = contextContaining(key);
 126         if (context == null)
 127             return null;
 128         else
 129             return context.value;
 130     }
 131 
 132     /**
 133      * <p>Check whether a value with the given key exists in the stack.
 134      * This means that the <code>push</code> method was called with
 135      * this key and it was not cancelled by a subsequent
 136      * <code>restore</code>.  This method is useful when the
 137      * <code>get</code> method returns null, to distinguish between
 138      * the case where the key exists in the stack but is associated
 139      * with a null value, and the case where the key does not exist in
 140      * the stack.</p>
 141      *
 142      * @return true if the key exists in the stack.
 143      *
 144      * @exception IllegalArgumentException if <code>key</code> is null.
 145      */
 146     public static boolean contains(String key)
 147             throws IllegalArgumentException {
 148         return (contextContaining(key) != null);
 149     }
 150 
 151     /**
 152      * <p>Find the ThreadContext in the stack that contains the given key,
 153      * or return null if there is none.</p>
 154      *
 155      * @exception IllegalArgumentException if <code>key</code> is null.
 156      */
 157     private static ThreadContext contextContaining(String key)
 158             throws IllegalArgumentException {
 159         if (key == null)
 160             throw new IllegalArgumentException("null key");
 161         for (ThreadContext context = getContext();
 162              context != null;
 163              context = context.previous) {
 164             if (key.equals(context.key))
 165                 return context;
 166             /* Note that "context.key" may be null if "context" is the
 167                sentinel, so don't write "if (context.key.equals(key))"!  */
 168         }
 169         return null;
 170     }
 171 
 172 //  /**
 173 //   * Change the value that was most recently associated with the given key
 174 //   * in a <code>push</code> operation not cancelled by a subsequent
 175 //   * <code>restore</code>.  If there is no such association, nothing happens
 176 //   * and the return value is null.
 177 //   *
 178 //   * @param key the key of interest.
 179 //   * @param value the new value to associate with that key.
 180 //   *
 181 //   * @return the value that was previously associated with the key, or null
 182 //   * if the key does not exist in the stack.
 183 //   *
 184 //   * @exception IllegalArgumentException if <code>key</code> is null.
 185 //   */
 186 //  public static Object set(String key, Object value)
 187 //          throws IllegalArgumentException {
 188 //      ThreadContext context = contextContaining(key);
 189 //      if (context == null)
 190 //          return null;
 191 //      Object old = context.value;
 192 //      context.value = value;
 193 //      return old;
 194 //  }
 195 
 196     /**
 197      * <p>Push an object on the context stack with the given key.
 198      * This operation can subsequently be undone by calling
 199      * <code>restore</code> with the ThreadContext value returned
 200      * here.</p>
 201      *
 202      * @param key the key that will be used to find the object while it is
 203      * on the stack.
 204      * @param value the value to be associated with that key.  It may be null.
 205      *
 206      * @return a ThreadContext that can be given to <code>restore</code> to
 207      * restore the stack to its state before the <code>push</code>.
 208      *
 209      * @exception IllegalArgumentException if <code>key</code> is null.
 210      */
 211     public static ThreadContext push(String key, Object value)
 212             throws IllegalArgumentException {
 213         if (key == null)
 214             throw new IllegalArgumentException("null key");
 215 
 216         ThreadContext oldContext = getContext();
 217         if (oldContext == null)
 218             oldContext = new ThreadContext(null, null, null);  // make sentinel
 219         ThreadContext newContext = new ThreadContext(oldContext, key, value);
 220         setContext(newContext);
 221         return oldContext;
 222     }
 223 
 224     /**
 225      * <p>Return an object that can later be supplied to <code>restore</code>
 226      * to restore the context stack to its current state.  The object can
 227      * also be given to <code>setInitialContext</code>.</p>
 228      *
 229      * @return a ThreadContext that represents the current context stack.
 230      */
 231     public static ThreadContext getThreadContext() {
 232         return getContext();
 233     }
 234 
 235     /**
 236      * <p>Restore the context stack to an earlier state.  This typically
 237      * undoes the effect of one or more <code>push</code> calls.</p>
 238      *
 239      * @param oldContext the state to return.  This is usually the return
 240      * value of an earlier <code>push</code> operation.
 241      *
 242      * @exception NullPointerException if <code>oldContext</code> is null.
 243      * @exception IllegalArgumentException if <code>oldContext</code>
 244      * does not represent a context from this thread, or if that
 245      * context was undone by an earlier <code>restore</code>.
 246      */
 247     public static void restore(ThreadContext oldContext)
 248             throws NullPointerException, IllegalArgumentException {
 249         /* The following test is not strictly necessary in the code as it
 250            stands today, since the reference to "oldContext.key" would
 251            generate a NullPointerException anyway.  But if someone
 252            didn't notice that during subsequent changes, they could
 253            accidentally permit restore(null) with the semantics of
 254            trashing the context stack.  */
 255         if (oldContext == null)
 256             throw new NullPointerException();
 257 
 258         /* Check that the restored context is in the stack.  */
 259         for (ThreadContext context = getContext();
 260              context != oldContext;
 261              context = context.previous) {
 262             if (context == null) {
 263                 throw new IllegalArgumentException("Restored context is not " +
 264                                                    "contained in current " +
 265                                                    "context");
 266             }
 267         }
 268 
 269         /* Discard the sentinel if the stack is empty.  This means that it
 270            is an error to call "restore" a second time with the
 271            ThreadContext value that means an empty stack.  That's why we
 272            don't say that it is all right to restore the stack to the
 273            state it was already in.  */
 274         if (oldContext.key == null)
 275             oldContext = null;
 276 
 277         setContext(oldContext);
 278     }
 279 
 280     /**
 281      * <p>Set the initial context of the calling thread to a context obtained
 282      * from another thread.  After this call, the calling thread will see
 283      * the same results from the <code>get</code> method as the thread
 284      * from which the <code>context</code> argument was obtained, at the
 285      * time it was obtained.</p>
 286      *
 287      * <p>The <code>context</code> argument must be the result of an earlier
 288      * <code>push</code> or <code>getThreadContext</code> call.  It is an
 289      * error (which may or may not be detected) if this context has been
 290      * undone by a <code>restore</code>.</p>
 291      *
 292      * <p>The context stack of the calling thread must be empty before this
 293      * call, i.e., there must not have been a <code>push</code> not undone
 294      * by a subsequent <code>restore</code>.</p>
 295      *
 296      * @exception IllegalArgumentException if the context stack was
 297      * not empty before the call.  An implementation may also throw this
 298      * exception if <code>context</code> is no longer current in the
 299      * thread from which it was obtained.
 300      */
 301     /* We rely on the fact that ThreadContext objects are immutable.
 302        This means that we don't have to check that the "context"
 303        argument is valid.  It necessarily represents the head of a
 304        valid chain of ThreadContext objects, even if the thread from
 305        which it was obtained has subsequently been set to a point
 306        later in that chain using "restore".  */
 307     public void setInitialContext(ThreadContext context)
 308             throws IllegalArgumentException {
 309         /* The following test assumes that we discard sentinels when the
 310            stack is empty.  */
 311         if (getContext() != null)
 312             throw new IllegalArgumentException("previous context not empty");
 313         setContext(context);
 314     }
 315 
 316     private static ThreadContext getContext() {
 317         return localContext.get();
 318     }
 319 
 320     private static void setContext(ThreadContext context) {
 321         localContext.set(context);
 322     }
 323 
 324     private static ThreadLocal<ThreadContext> localContext =
 325             new ThreadLocal<ThreadContext>();
 326 }