View Javadoc

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