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.subject.support.SubjectRunnable;
20 
21 import hunt.shiro.subject.support.SubjectThreadState;
22 
23 import hunt.shiro.subject.Subject;
24 import hunt.shiro.util.ThreadState;
25 
26 import hunt.Exceptions;
27 import hunt.util.Common;
28 import hunt.util.Runnable;
29 
30 
31 /**
32  * A {@code SubjectRunnable} ensures that a target/delegate {@link Runnable Runnable} will execute such that any
33  * call to {@code SecurityUtils.}{@link hunt.shiro.SecurityUtils#getSubject() getSubject()} during the
34  * {@code Runnable}'s execution will return the associated {@code Subject} instance.  The {@code SubjectRunnable}
35  * instance can be run on any thread (the current thread or asynchronously on another thread) and the
36  * {@code SecurityUtils.getSubject()} call will still work properly.  This implementation also guarantees that Shiro's
37  * thread state will be identical before and after execution to ensure threads remain clean in any thread-pooled
38  * environment.
39  * <p/>
40  * When instances of this class {@link Runnable#run() run()}, the following occurs:
41  * <ol>
42  * <li>The Subject and any of its associated thread state is first bound to the thread that executes the
43  * {@code Runnable}.</li>
44  * <li>The delegate/target {@code Runnable} is {@link #doRun(Runnable) run}</li>
45  * <li>Any previous thread state that might have existed before the {@code Subject} was bound is fully restored</li>
46  * </ol>
47  * <p/>
48  *
49  * <h3>Usage</h3>
50  *
51  * This is typically considered a support class and is not often directly referenced.  Most people prefer to use
52  * the {@code Subject.}{@link Subject#execute(Runnable) execute} or
53  * {@code Subject.}{@link Subject#associateWith(Runnable) associateWith} methods, which transparently perform the
54  * necessary association logic.
55  * <p/>
56  * An even more convenient alternative is to use a
57  * {@link hunt.shiro.concurrent.SubjectAwareExecutor SubjectAwareExecutor}, which transparently uses
58  * instances of this class but does not require referencing Shiro's API at all.
59  *
60  * @see Subject#associateWith(Runnable)
61  * @see hunt.shiro.concurrent.SubjectAwareExecutor SubjectAwareExecutor
62  */
63 class SubjectRunnable : Runnable {
64 
65     protected ThreadState threadState;
66     private Runnable runnable;
67 
68     /**
69      * Creates a new {@code SubjectRunnable} that, when executed, will execute the target {@code delegate}, but
70      * guarantees that it will run associated with the specified {@code Subject}.
71      *
72      * @param subject  the Subject to associate with the delegate's execution.
73      * @param delegate the runnable to run.
74      */
75     this(Subject subject, Runnable dg) {
76         this(new SubjectThreadState(subject), dg);
77     }
78 
79     /**
80      * Creates a new {@code SubjectRunnable} that, when executed, will perform thread state
81      * {@link ThreadState#bind binding} and guaranteed {@link ThreadState#restore restoration} before and after the
82      * {@link Runnable Runnable}'s execution, respectively.
83      *
84      * @param threadState the thread state to bind and unbind before and after the runnable's execution.
85      * @param dg    the delegate {@code Runnable} to execute when this instance is {@link #run() run()}.
86      * @throws IllegalArgumentException if either the {@code ThreadState} or {@link Runnable} arguments are {@code null}.
87      */
88     protected this(ThreadState threadState, Runnable dg){
89         if (threadState  is null) {
90             throw new IllegalArgumentException("ThreadState argument cannot be null.");
91         }
92         this.threadState = threadState;
93         if (dg  is null) {
94             throw new IllegalArgumentException("Runnable argument cannot be null.");
95         }
96         this.runnable = dg;
97     }
98 
99     /**
100      * {@link ThreadState#bind Bind}s the Subject thread state, executes the target {@code Runnable} and then guarantees
101      * the previous thread state's {@link ThreadState#restore restoration}:
102      * <pre>
103      * try {
104      *     threadState.{@link ThreadState#bind bind()};
105      *     {@link #doRun doRun}(targetRunnable);
106      * } finally {
107      *     threadState.{@link ThreadState#restore restore()}
108      * }
109      * </pre>
110      */
111      void run() {
112         try {
113             threadState.bind();
114             doRun(this.runnable);
115         } finally {
116             threadState.restore();
117         }
118     }
119 
120     /**
121      * Simply calls the target {@link Runnable Runnable}'s {@link Runnable#run run()} method.
122      *
123      * @param runnable the target runnable to run.
124      */
125     protected void doRun(Runnable runnable) {
126         runnable.run();
127     }
128 }