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.concurrent;
20  
21  import org.apache.shiro.subject.Subject;
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.concurrent.*;
27  
28  /**
29   * {@code ExecutorService} implementation that will automatically first associate any argument
30   * {@link Runnable} or {@link Callable} instances with the {@link #getSubject currently available subject} and then
31   * dispatch the Subject-enabled runnable or callable to an underlying delegate
32   * {@link java.util.concurrent.ExecutorService ExecutorService} instance.  The principle is the same as the
33   * parent {@link SubjectAwareExecutor} class, but enables the richer {@link ExecutorService} API.
34   * <p/>
35   * This is a simplification for applications that want to execute code as the currently
36   * executing {@code Subject} on another thread, but don't want or need to call the
37   * {@link Subject#associateWith(Runnable)} or {@link Subject#associateWith(Callable)} methods and dispatch them to a
38   * Thread manually.  This simplifies code and reduces Shiro dependencies across application source code.
39   * <p/>
40   * Consider this code that could be repeated in many places across an application:
41   * <pre>
42   * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
43   * {@link Subject Subject} subject = {@link org.apache.shiro.SecurityUtils SecurityUtils}.{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()};
44   * {@link Callable Callable} work = subject.{@link Subject#associateWith(Callable) associateWith(applicationWork)};
45   * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
46   * </pre>
47   * Instead, if the {@code ExecutorService} instance used at runtime is an instance of this class
48   * (which delegates to the target ExecutorService that you want), all places in code like the above reduce to this:
49   * <pre>
50   * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
51   * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
52   * </pre>
53   * Notice there is no use of the Shiro API in the 2nd code block, encouraging the principle of loose coupling across
54   * your codebase.
55   *
56   * @since 1.0
57   */
58  public class SubjectAwareExecutorService extends SubjectAwareExecutor implements ExecutorService {
59  
60      private ExecutorService targetExecutorService;
61  
62      public SubjectAwareExecutorService() {
63      }
64  
65      public SubjectAwareExecutorService(ExecutorService target) {
66          setTargetExecutorService(target);
67      }
68  
69      public ExecutorService getTargetExecutorService() {
70          return targetExecutorService;
71      }
72  
73      public void setTargetExecutorService(ExecutorService targetExecutorService) {
74          super.setTargetExecutor(targetExecutorService);
75          this.targetExecutorService = targetExecutorService;
76      }
77  
78      @Override
79      public void setTargetExecutor(Executor targetExecutor) {
80          if (!(targetExecutor instanceof ExecutorService)) {
81              String msg = "The " + getClass().getName() + " implementation only accepts " +
82                      ExecutorService.class.getName() + " target instances.";
83              throw new IllegalArgumentException(msg);
84          }
85          super.setTargetExecutor(targetExecutor);
86      }
87  
88      public void shutdown() {
89          this.targetExecutorService.shutdown();
90      }
91  
92      public List<Runnable> shutdownNow() {
93          return this.targetExecutorService.shutdownNow();
94      }
95  
96      public boolean isShutdown() {
97          return this.targetExecutorService.isShutdown();
98      }
99  
100     public boolean isTerminated() {
101         return this.targetExecutorService.isTerminated();
102     }
103 
104     public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
105         return this.targetExecutorService.awaitTermination(timeout, unit);
106     }
107 
108     protected <T> Callable<T> associateWithSubject(Callable<T> task) {
109         Subject subject = getSubject();
110         return subject.associateWith(task);
111     }
112 
113     public <T> Future<T> submit(Callable<T> task) {
114         Callable<T> work = associateWithSubject(task);
115         return this.targetExecutorService.submit(work);
116     }
117 
118     public <T> Future<T> submit(Runnable task, T result) {
119         Runnable work = associateWithSubject(task);
120         return this.targetExecutorService.submit(work, result);
121     }
122 
123     public Future<?> submit(Runnable task) {
124         Runnable work = associateWithSubject(task);
125         return this.targetExecutorService.submit(work);
126     }
127 
128     protected <T> Collection<Callable<T>> associateWithSubject(Collection<? extends Callable<T>> tasks) {
129         Collection<Callable<T>> workItems = new ArrayList<Callable<T>>(tasks.size());
130         for (Callable<T> task : tasks) {
131             Callable<T> work = associateWithSubject(task);
132             workItems.add(work);
133         }
134         return workItems;
135     }
136 
137     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
138         Collection<Callable<T>> workItems = associateWithSubject(tasks);
139         return this.targetExecutorService.invokeAll(workItems);
140     }
141 
142     public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
143             throws InterruptedException {
144         Collection<Callable<T>> workItems = associateWithSubject(tasks);
145         return this.targetExecutorService.invokeAll(workItems, timeout, unit);
146     }
147 
148     public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
149         Collection<Callable<T>> workItems = associateWithSubject(tasks);
150         return this.targetExecutorService.invokeAny(workItems);
151     }
152 
153     public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
154             throws InterruptedException, ExecutionException, TimeoutException {
155         Collection<Callable<T>> workItems = associateWithSubject(tasks);
156         return this.targetExecutorService.invokeAny(workItems, timeout, unit);
157     }
158 }