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 }