KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > excalibur > lang > LayeredThreadContext


1 /*
2  * Copyright (C) The Apache Software Foundation. All rights reserved.
3  *
4  * This software is published under the terms of the Apache Software License
5  * version 1.1, a copy of which has been included with this distribution in
6  * the LICENSE.txt file.
7  */

8 package org.apache.avalon.excalibur.lang;
9
10 import java.util.HashMap JavaDoc;
11 import java.util.Map JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.ArrayList JavaDoc;
14
15 /**
16  * The <code>LayeredThreadContext</code> defines a set of data
17  * that is associated with a particular thread. This is a useful
18  * feature for many of the same reasons as <code>ThreadContext</code>
19  * is useful.
20  *
21  * <p>The difference between <code>LayeredThreadContext</code> and
22  * <code>ThreadContext</code> is the presence of Layers. When accessing
23  * a variable from the context the system will check the top layer. If
24  * the variable is not in the top layer it will check the layer underneath
25  * and so on until the variable is found or there are no more layers or
26  * a "divider" layer occurs.</p>
27  *
28  * <p>These variables are managed by the LayeredThreadContext. However as it
29  * is not possible to inject code to be executed at the start of a
30  * thread, new threads may not be activated and the variables may not
31  * be set appropriately. In such cases it is recomended that the developer
32  * use <code>InheritableThreadLocal</code> as the underlying representation
33  * of the variable. By doing this the <code>InheritableThreadLocal</code>
34  * will maintain the appropriate state in newly created Thread.</p>
35  *
36  * <p>The policy chosend to manage such state is pluggable by the user.
37  * It is expected developers will provide a policy object that will manage
38  * thread local variables. For instance an application server may choose to
39  * keep the name of the application, the Subject it is running as and perhaps
40  * other state in such variables.</p>
41  *
42  * <p>Note that the idea for this code was derived from the HPs CSF framework.</p>
43  *
44  * @author <a HREF="mailto:peter@apache.org">Peter Donald</a>
45  */

46 public final class LayeredThreadContext
47 {
48     ///Permission used to guard setThreadContext method.
49
private final static RuntimePermission JavaDoc c_setThreadContextPermission =
50         new RuntimePermission JavaDoc( "LayeredThreadContext.setThreadContext" );
51
52     ///Permission used to guard push method.
53
private final static RuntimePermission JavaDoc c_pushPermission =
54         new RuntimePermission JavaDoc( "LayeredThreadContext.push" );
55
56     private final static InheritableThreadLocal JavaDoc c_context = new CloningInheritableThreadLocal();
57
58     ///Accessor object used to provide policy with access to variables in context
59
private final ThreadContextAccessor m_accessor = new InnerThreadContextAccessor();
60
61     ///The policy for ThreadContext
62
private final ThreadContextPolicy m_policy;
63
64     ///The list of Layers
65
private final ArrayList JavaDoc m_layers = new ArrayList JavaDoc();
66
67     /**
68      * Retrieve the LayeredThreadContext associated with the current thread.
69      *
70      * @return the LayeredThreadContext associated with the current thread.
71      */

72     public static LayeredThreadContext getThreadContext()
73     {
74         return (LayeredThreadContext)c_context.get();
75     }
76
77     /**
78      * Set the LayeredThreadContext associated with the current thread.
79      * This code will also call <code>deactivate()</code> on the old
80      * <code>LayeredThreadContext</code> if present and <code>activate()</code>
81      * on new <code>LayeredThreadContext</code> (if not null).
82      *
83      * @param threadContext the new LayeredThreadContext
84      * @exception SecurityException if the caller does not have permission to set thread pool
85      */

86     public static void setThreadContext( final LayeredThreadContext threadContext )
87         throws SecurityException JavaDoc
88     {
89         final SecurityManager JavaDoc securityManager = System.getSecurityManager();
90         if( null != securityManager )
91         {
92             securityManager.checkPermission( c_setThreadContextPermission );
93         }
94
95         final LayeredThreadContext oldThreadContext = (LayeredThreadContext)c_context.get();
96         if( null != oldThreadContext ) oldThreadContext.deactivate();
97
98         c_context.set( threadContext );
99         if( null != threadContext )
100         {
101             threadContext.activate();
102         }
103     }
104
105     /**
106      * Default constructor to create an empty <code>LayeredThreadContext</code>
107      * with specified policy.
108      */

109     public LayeredThreadContext( final ThreadContextPolicy policy )
110     {
111         if( null == policy )
112         {
113             throw new NullPointerException JavaDoc( "policy property is null" );
114         }
115
116         m_policy = policy;
117     }
118
119     /**
120      * Pop layers off the top of context stack until you
121      * find a layer that has a key that matches specified key.
122      *
123      * <p>Note that no security check is performed because it is expected
124      * that anyone with the correct key is allowed to call pop() for the
125      * matching layer.</p>
126      *
127      * @param key the layer key
128      * @exception IllegalArgumentException if no layer matches key
129      */

130     public synchronized void pop( final Object JavaDoc key )
131         throws IllegalArgumentException JavaDoc
132     {
133         final int size = m_layers.size();
134         final int top = size - 1;
135         for( int i = top; i >= 0; i-- )
136         {
137             final Layer layer = (Layer)m_layers.get( i );
138             if( layer.matchesKey( key ) )
139             {
140                 for( int j = top; j >= i; j-- )
141                 {
142                     m_layers.remove( j );
143                 }
144                 return;
145             }
146         }
147
148         throw new IllegalArgumentException JavaDoc( "Unknown key " + key );
149     }
150
151     /**
152      * Push a layer onto context stack with specified key.
153      * Add entries to layer as specified in map and make the layer
154      * a "divider" layer if isDivider == true.
155      *
156      * @param key the layer key
157      * @param map the map of values to place in layer
158      * @param isDivider true if the layer is to be a divider layer
159      * @exception IllegalArgumentException if the map contains invalid entries
160      */

161     public synchronized void push( final Object JavaDoc key, final Map JavaDoc map, final boolean isDivider )
162         throws SecurityException JavaDoc, IllegalArgumentException JavaDoc
163     {
164         final SecurityManager JavaDoc securityManager = System.getSecurityManager();
165         if( null != securityManager )
166         {
167             securityManager.checkPermission( c_pushPermission );
168         }
169
170         final Map JavaDoc data = new HashMap JavaDoc();
171
172         final Iterator JavaDoc keys = map.keySet().iterator();
173         while( keys.hasNext() )
174         {
175             final Object JavaDoc keyValue = keys.next();
176             final Object JavaDoc value = map.get( keyValue );
177             final String JavaDoc keyString = keyValue.toString();
178
179             m_policy.verifyKeyValue( keyString, value );
180             data.put( keyString, value );
181         }
182
183         m_layers.add( new Layer( key, data, isDivider ) );
184     }
185
186     /**
187      * Utility method to search layers to retrieve value for key.
188      *
189      * @param key the key to look for
190      * @return the Object found or null
191      */

192     protected synchronized Object JavaDoc contextGet( final String JavaDoc key )
193     {
194         final int size = m_layers.size();
195         final int top = size - 1;
196         for( int i = top; i >= 0; i-- )
197         {
198             final Layer layer = (Layer)m_layers.get( i );
199             if( layer.containsKey( key ) )
200             {
201                 return layer.get( key );
202             }
203             
204             //Failed to locate key before divider so return null
205
if( layer.isDivider() ) break;
206         }
207
208         return null;
209     }
210
211     /**
212      * Utility method to search layers to see if key is found.
213      *
214      * @param key the key to look for
215      * @return true if key found, false otherwise
216      */

217     protected synchronized boolean contextContainsKey( final String JavaDoc key )
218     {
219         final int size = m_layers.size();
220         final int top = size - 1;
221         for( int i = top; i >= 0; i-- )
222         {
223             final Layer layer = (Layer)m_layers.get( i );
224             if( layer.containsKey( key ) )
225             {
226                 return true;
227             }
228             
229             //Failed to locate key before divider so return false
230
if( layer.isDivider() ) break;
231         }
232
233         return false;
234     }
235
236     /**
237      * Method to allow duplication of ThreadContext for new threads.
238      *
239      * @return the duplicated context
240      */

241     protected synchronized LayeredThreadContext duplicate()
242     {
243         final LayeredThreadContext context = new LayeredThreadContext( m_policy );
244         
245         final int size = m_layers.size();
246         for( int i = 0; i < size; i++ )
247         {
248             final Layer layer = (Layer)m_layers.get( i );
249
250             //Layers can be shared because they are immutable
251
context.m_layers.add( layer );
252         }
253
254         return context;
255     }
256
257     /**
258      * Utility method to call activate on policy object.
259      */

260     private void activate()
261     {
262         m_policy.activate( m_accessor );
263     }
264
265     /**
266      * Utility method to call deactivate on policy object.
267      */

268     private void deactivate()
269     {
270         m_policy.deactivate( m_accessor );
271     }
272
273     /**
274      * Class that manages inheritance of ThreadContext between threads.
275      */

276     private static class CloningInheritableThreadLocal extends InheritableThreadLocal JavaDoc
277     {
278         protected Object JavaDoc childValue( final Object JavaDoc parentValue )
279         {
280             final LayeredThreadContext context = (LayeredThreadContext)parentValue;
281             return context.duplicate();
282         }
283     }
284
285     /**
286      * Inner class to offer accessor interface to policy object.
287      */

288     private class InnerThreadContextAccessor implements ThreadContextAccessor
289     {
290         public boolean containsKey( final String JavaDoc key )
291         {
292             return contextContainsKey( key );
293         }
294
295         public Object JavaDoc get( final String JavaDoc key )
296         {
297             return contextGet( key );
298         }
299     }
300 }
301
Popular Tags