jdk/src/jdk.snmp/share/classes/com/sun/jmx/snmp/ThreadContext.java
changeset 27285 1b4ca1c84d23
parent 27284 98699328cef3
parent 27263 819f5f87d485
child 27288 a3f652a37d1e
child 27503 b75a635f1ff3
equal deleted inserted replaced
27284:98699328cef3 27285:1b4ca1c84d23
     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 }