001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.concurrent;
020
021 import org.apache.shiro.subject.Subject;
022
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.List;
026 import java.util.concurrent.*;
027
028 /**
029 * {@code ExecutorService} implementation that will automatically first associate any argument
030 * {@link Runnable} or {@link Callable} instances with the {@link #getSubject currently available subject} and then
031 * dispatch the Subject-enabled runnable or callable to an underlying delegate
032 * {@link java.util.concurrent.ExecutorService ExecutorService} instance. The principle is the same as the
033 * parent {@link SubjectAwareExecutor} class, but enables the richer {@link ExecutorService} API.
034 * <p/>
035 * This is a simplification for applications that want to execute code as the currently
036 * executing {@code Subject} on another thread, but don't want or need to call the
037 * {@link Subject#associateWith(Runnable)} or {@link Subject#associateWith(Callable)} methods and dispatch them to a
038 * Thread manually. This simplifies code and reduces Shiro dependencies across application source code.
039 * <p/>
040 * Consider this code that could be repeated in many places across an application:
041 * <pre>
042 * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
043 * {@link Subject Subject} subject = {@link org.apache.shiro.SecurityUtils SecurityUtils}.{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()};
044 * {@link Callable Callable} work = subject.{@link Subject#associateWith(Callable) associateWith(applicationWork)};
045 * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
046 * </pre>
047 * Instead, if the {@code ExecutorService} instance used at runtime is an instance of this class
048 * (which delegates to the target ExecutorService that you want), all places in code like the above reduce to this:
049 * <pre>
050 * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
051 * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
052 * </pre>
053 * Notice there is no use of the Shiro API in the 2nd code block, encouraging the principle of loose coupling across
054 * your codebase.
055 *
056 * @since 1.0
057 */
058 public class SubjectAwareExecutorService extends SubjectAwareExecutor implements ExecutorService {
059
060 private ExecutorService targetExecutorService;
061
062 public SubjectAwareExecutorService() {
063 }
064
065 public SubjectAwareExecutorService(ExecutorService target) {
066 setTargetExecutorService(target);
067 }
068
069 public ExecutorService getTargetExecutorService() {
070 return targetExecutorService;
071 }
072
073 public void setTargetExecutorService(ExecutorService targetExecutorService) {
074 super.setTargetExecutor(targetExecutorService);
075 this.targetExecutorService = targetExecutorService;
076 }
077
078 @Override
079 public void setTargetExecutor(Executor targetExecutor) {
080 if (!(targetExecutor instanceof ExecutorService)) {
081 String msg = "The " + getClass().getName() + " implementation only accepts " +
082 ExecutorService.class.getName() + " target instances.";
083 throw new IllegalArgumentException(msg);
084 }
085 super.setTargetExecutor(targetExecutor);
086 }
087
088 public void shutdown() {
089 this.targetExecutorService.shutdown();
090 }
091
092 public List<Runnable> shutdownNow() {
093 return this.targetExecutorService.shutdownNow();
094 }
095
096 public boolean isShutdown() {
097 return this.targetExecutorService.isShutdown();
098 }
099
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 }