1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.util.ThreadContext;
20 
21 import hunt.shiro.util.CollectionUtils;
22 
23 import hunt.shiro.mgt.SecurityManager;
24 import hunt.shiro.subject.Subject;
25 import hunt.logging.Logger;
26 
27 import hunt.collection.Collections;
28 import hunt.collection.HashMap;
29 import hunt.collection.Map;
30 import hunt.Exceptions;
31 
32 import core.thread;
33 import std.traits;
34 
35 /**
36  * A ThreadContext provides a means of binding and unbinding objects to the
37  * current thread based on key/value pairs.
38  * <p/>
39  * <p>An internal {@link java.util.HashMap} is used to maintain the key/value pairs
40  * for each thread.</p>
41  * <p/>
42  * <p>If the desired behavior is to ensure that bound data is not shared across
43  * threads in a pooled or reusable threaded environment, the application (or more likely a framework) must
44  * bind and remove any necessary values at the beginning and end of stack
45  * execution, respectively (i.e. individually explicitly or all via the <tt>clear</tt> method).</p>
46  *
47  * @see #remove()
48  */
49 abstract class ThreadContext {
50 
51     /**
52      * Private internal log instance.
53      */
54 
55 
56      enum string SECURITY_MANAGER_KEY = fullyQualifiedName!(ThreadContext) ~ "_SECURITY_MANAGER_KEY";
57      enum string SUBJECT_KEY = fullyQualifiedName!(ThreadContext) ~ "_SUBJECT_KEY";
58 
59     private static Map!(string, Object) resources;
60 
61 
62     /**
63      * Default no-argument constructor.
64      */
65     protected this() {
66     }
67 
68     /**
69      * Returns the ThreadLocal Map. This Map is used internally to bind objects
70      * to the current thread by storing each object under a unique key.
71      *
72      * @return the map of bound resources
73      */
74      static Map!(string, Object) getResources() {
75         if (resources is null){
76             return Collections.emptyMap!(string, Object)();
77         } else {
78             return new HashMap!(string, Object)(resources);
79         }
80     }
81 
82     /**
83      * Allows a caller to explicitly set the entire resource map.  This operation overwrites everything that existed
84      * previously in the ThreadContext - if you need to retain what was on the thread prior to calling this method,
85      * call the {@link #getResources()} method, which will give you the existing state.
86      *
87      * @param newResources the resources to replace the existing {@link #getResources() resources}.
88      */
89      static void setResources(Map!(string, Object) newResources) {
90         if (CollectionUtils.isEmpty!(string, Object)(newResources)) {
91             return;
92         }
93         ensureResourcesInitialized();
94         resources.clear();
95         resources.putAll(newResources);
96     }
97 
98     /**
99      * Returns the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
100      * is no value for that {@code key}.
101      *
102      * @param key the map key to use to lookup the value
103      * @return the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there
104      *         is no value for that {@code key}.
105      */
106     private static Object getValue(string key) {
107         Map!(string, Object) perThreadResources = resources;
108         return perThreadResources !is null ? perThreadResources.get(key) : null;
109     }
110 
111     private static void ensureResourcesInitialized(){
112         if (resources  is null){
113            resources = new HashMap!(string, Object)();
114         }
115     }
116 
117     /**
118      * Returns the object for the specified <code>key</code> that is bound to
119      * the current thread.
120      *
121      * @param key the key that identifies the value to return
122      * @return the object keyed by <code>key</code> or <code>null</code> if
123      *         no value exists for the specified <code>key</code>
124      */
125      static Object get(string key) {
126         version(HUNT_SHIRO_DEBUG) {
127             string msg = "get(%s) - in thread [" ~ Thread.getThis().name() ~ "]"; 
128             tracef(msg, key);
129         }
130 
131         Object value = getValue(key);
132         version(HUNT_SHIRO_DEBUG) {
133             if (value !is null) {
134                 msg = "Retrieved value of type [" ~ typeid(value).name ~ "] for key [" ~
135                         key ~ "] " ~ "bound to thread [" ~ Thread.getThis().name() ~ "]";
136                 tracef(msg);
137             }
138         }
139         return value;
140     }
141 
142     /**
143      * Binds <tt>value</tt> for the given <code>key</code> to the current thread.
144      * <p/>
145      * <p>A <tt>null</tt> <tt>value</tt> has the same effect as if <tt>remove</tt> was called for the given
146      * <tt>key</tt>, i.e.:
147      * <p/>
148      * <pre>
149      * if ( value  is null ) {
150      *     remove( key );
151      * }</pre>
152      *
153      * @param key   The key with which to identify the <code>value</code>.
154      * @param value The value to bind to the thread.
155      * @throws IllegalArgumentException if the <code>key</code> argument is <tt>null</tt>.
156      */
157      static void put(string key, Object value) {
158         if (key  is null) {
159             throw new IllegalArgumentException("key cannot be null");
160         }
161 
162         if (value  is null) {
163             remove(key);
164             return;
165         }
166 
167         ensureResourcesInitialized();
168         resources.put(key, value);
169 
170         version(HUNT_SHIRO_DEBUG) {
171             string msg = "Bound value of type [" ~ typeid(value).name ~ "] for key [" ~
172                     key ~ "] to thread " ~ "[" ~ Thread.getThis().name() ~ "]";
173             tracef(msg);
174         }
175     }
176 
177     /**
178      * Unbinds the value for the given <code>key</code> from the current
179      * thread.
180      *
181      * @param key The key identifying the value bound to the current thread.
182      * @return the object unbound or <tt>null</tt> if there was nothing bound
183      *         under the specified <tt>key</tt> name.
184      */
185      static Object remove(string key) {
186         Map!(string, Object) perThreadResources = resources;
187         Object value = perThreadResources !is null ? perThreadResources.remove(key) : null;
188 
189         version(HUNT_SHIRO_DEBUG) {
190             if (value !is null) {
191                 string msg = "Removed value of type [" ~ typeid(cast(Object)value).name ~ "] for key [" ~
192                         key ~ "]" ~ "from thread [" ~ Thread.getThis().name() ~ "]";
193                 tracef(msg);
194             }
195         }
196 
197         return value;
198     }
199 
200     /**
201      * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal ThreadLocal} from the thread.
202      * <p/>
203      * This method is meant to be the final 'clean up' operation that is called at the end of thread execution to
204      * prevent thread corruption in pooled thread environments.
205      *
206      */
207     static void remove() {
208         resources = null;
209     }
210 
211     /**
212      * Convenience method that simplifies retrieval of the application's SecurityManager instance from the current
213      * thread. If there is no SecurityManager bound to the thread (probably because framework code did not bind it
214      * to the thread), this method returns <tt>null</tt>.
215      * <p/>
216      * It is merely a convenient wrapper for the following:
217      * <p/>
218      * <code>return (SecurityManager)get( SECURITY_MANAGER_KEY );</code>
219      * <p/>
220      * This method only returns the bound value if it exists - it does not remove it
221      * from the thread.  To remove it, one must call {@link #unbindSecurityManager() unbindSecurityManager()} instead.
222      *
223      * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
224      */
225      static SecurityManager getSecurityManager() {
226         return cast(SecurityManager) get(SECURITY_MANAGER_KEY);
227     }
228 
229 
230     /**
231      * Convenience method that simplifies binding the application's SecurityManager instance to the ThreadContext.
232      * <p/>
233      * <p>The method's existence is to help reduce casting in code and to simplify remembering of
234      * ThreadContext key names.  The implementation is simple in that, if the SecurityManager is not <tt>null</tt>,
235      * it binds it to the thread, i.e.:
236      * <p/>
237      * <pre>
238      * if (securityManager !is null) {
239      *     put( SECURITY_MANAGER_KEY, securityManager);
240      * }</pre>
241      *
242      * @param securityManager the application's SecurityManager instance to bind to the thread.  If the argument is
243      *                        null, nothing will be done.
244      */
245     static void bind(SecurityManager securityManager) {
246         if (securityManager !is null) {
247             put(SECURITY_MANAGER_KEY, cast(Object)securityManager);
248         }
249     }
250 
251     /**
252      * Convenience method that simplifies removal of the application's SecurityManager instance from the thread.
253      * <p/>
254      * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
255      * merely a convenient wrapper for the following:
256      * <p/>
257      * <code>return (SecurityManager)remove( SECURITY_MANAGER_KEY );</code>
258      * <p/>
259      * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later
260      * during thread execution), use the {@link #getSecurityManager() getSecurityManager()} method instead.
261      *
262      * @return the application's SecurityManager instance previously bound to the thread, or <tt>null</tt> if there
263      *         was none bound.
264      */
265      static SecurityManager unbindSecurityManager() {
266         return cast(SecurityManager) remove(SECURITY_MANAGER_KEY);
267     }
268 
269     /**
270      * Convenience method that simplifies retrieval of a thread-bound Subject.  If there is no
271      * Subject bound to the thread, this method returns <tt>null</tt>.  It is merely a convenient wrapper
272      * for the following:
273      * <p/>
274      * <code>return (Subject)get( SUBJECT_KEY );</code>
275      * <p/>
276      * This method only returns the bound value if it exists - it does not remove it
277      * from the thread.  To remove it, one must call {@link #unbindSubject() unbindSubject()} instead.
278      *
279      * @return the Subject object bound to the thread, or <tt>null</tt> if there isn't one bound.
280      */
281     static Subject getSubject() {
282         return cast(Subject) get(SUBJECT_KEY);
283     }
284 
285     static Subject getSubject(string name) {
286         return cast(Subject) get(name ~ SUBJECT_KEY);
287     }
288 
289 
290     /**
291      * Convenience method that simplifies binding a Subject to the ThreadContext.
292      * <p/>
293      * <p>The method's existence is to help reduce casting in your own code and to simplify remembering of
294      * ThreadContext key names.  The implementation is simple in that, if the Subject is not <tt>null</tt>,
295      * it binds it to the thread, i.e.:
296      * <p/>
297      * <pre>
298      * if (subject !is null) {
299      *     put( SUBJECT_KEY, subject );
300      * }</pre>
301      *
302      * @param subject the Subject object to bind to the thread.  If the argument is null, nothing will be done.
303      */
304     static void bind(Subject subject) {
305         if (subject !is null) {
306             put(SUBJECT_KEY, cast(Object)subject);
307         }
308     }
309 
310     static void bind(string name, Subject subject) {
311         if (subject !is null) {
312             put(name ~ SUBJECT_KEY, cast(Object)subject);
313         }
314     }
315 
316     /**
317      * Convenience method that simplifies removal of a thread-local Subject from the thread.
318      * <p/>
319      * The implementation just helps reduce casting and remembering of the ThreadContext key name, i.e it is
320      * merely a convenient wrapper for the following:
321      * <p/>
322      * <code>return (Subject)remove( SUBJECT_KEY );</code>
323      * <p/>
324      * If you wish to just retrieve the object from the thread without removing it (so it can be retrieved later during
325      * thread execution), you should use the {@link #getSubject() getSubject()} method for that purpose.
326      *
327      * @return the Subject object previously bound to the thread, or <tt>null</tt> if there was none bound.
328      */
329      static Subject unbindSubject() {
330         return cast(Subject) remove(SUBJECT_KEY);
331     }
332     
333 }
334