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 */
019package org.apache.shiro.concurrent;
020
021import org.apache.shiro.subject.Subject;
022
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.List;
026import java.util.concurrent.Callable;
027import java.util.concurrent.ExecutionException;
028import java.util.concurrent.Executor;
029import java.util.concurrent.ExecutorService;
030import java.util.concurrent.Future;
031import java.util.concurrent.TimeUnit;
032import java.util.concurrent.TimeoutException;
033
034/**
035 * {@code ExecutorService} implementation that will automatically first associate any argument
036 * {@link Runnable} or {@link Callable} instances with the {@link #getSubject currently available subject} and then
037 * dispatch the Subject-enabled runnable or callable to an underlying delegate
038 * {@link java.util.concurrent.ExecutorService ExecutorService} instance.  The principle is the same as the
039 * parent {@link SubjectAwareExecutor} class, but enables the richer {@link ExecutorService} API.
040 * <p/>
041 * This is a simplification for applications that want to execute code as the currently
042 * executing {@code Subject} on another thread, but don't want or need to call the
043 * {@link Subject#associateWith(Runnable)} or {@link Subject#associateWith(Callable)} methods and dispatch them to a
044 * Thread manually.  This simplifies code and reduces Shiro dependencies across application source code.
045 * <p/>
046 * Consider this code that could be repeated in many places across an application:
047 * <pre>
048 * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
049 * {@link Subject Subject} subject = {@link org.apache.shiro.SecurityUtils SecurityUtils}.
050 * {@link org.apache.shiro.SecurityUtils#getSubject() getSubject()};
051 * {@link Callable Callable} work = subject.{@link Subject#associateWith(Callable) associateWith(applicationWork)};
052 * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
053 * </pre>
054 * Instead, if the {@code ExecutorService} instance used at runtime is an instance of this class
055 * (which delegates to the target ExecutorService that you want), all places in code like the above reduce to this:
056 * <pre>
057 * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
058 * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
059 * </pre>
060 * Notice there is no use of the Shiro API in the 2nd code block, encouraging the principle of loose coupling across
061 * your codebase.
062 *
063 * @since 1.0
064 */
065public class SubjectAwareExecutorService extends SubjectAwareExecutor implements ExecutorService {
066
067    private ExecutorService targetExecutorService;
068
069    public SubjectAwareExecutorService() {
070    }
071
072    public SubjectAwareExecutorService(ExecutorService target) {
073        setTargetExecutorService(target);
074    }
075
076    public ExecutorService getTargetExecutorService() {
077        return targetExecutorService;
078    }
079
080    public void setTargetExecutorService(ExecutorService targetExecutorService) {
081        super.setTargetExecutor(targetExecutorService);
082        this.targetExecutorService = targetExecutorService;
083    }
084
085    @Override
086    public void setTargetExecutor(Executor targetExecutor) {
087        if (!(targetExecutor instanceof ExecutorService)) {
088            String msg = "The " + getClass().getName() + " implementation only accepts "
089                    + ExecutorService.class.getName() + " target instances.";
090            throw new IllegalArgumentException(msg);
091        }
092        super.setTargetExecutor(targetExecutor);
093    }
094
095    public void shutdown() {
096        this.targetExecutorService.shutdown();
097    }
098
099    public List<Runnable> shutdownNow() {
100        return this.targetExecutorService.shutdownNow();
101    }
102
103    public boolean isShutdown() {
104        return this.targetExecutorService.isShutdown();
105    }
106
107    public boolean isTerminated() {
108        return this.targetExecutorService.isTerminated();
109    }
110
111    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
112        return this.targetExecutorService.awaitTermination(timeout, unit);
113    }
114
115    protected <T> Callable<T> associateWithSubject(Callable<T> task) {
116        Subject subject = getSubject();
117        return subject.associateWith(task);
118    }
119
120    public <T> Future<T> submit(Callable<T> task) {
121        Callable<T> work = associateWithSubject(task);
122        return this.targetExecutorService.submit(work);
123    }
124
125    public <T> Future<T> submit(Runnable task, T result) {
126        Runnable work = associateWithSubject(task);
127        return this.targetExecutorService.submit(work, result);
128    }
129
130    public Future<?> submit(Runnable task) {
131        Runnable work = associateWithSubject(task);
132        return this.targetExecutorService.submit(work);
133    }
134
135    protected <T> Collection<Callable<T>> associateWithSubject(Collection<? extends Callable<T>> tasks) {
136        Collection<Callable<T>> workItems = new ArrayList<Callable<T>>(tasks.size());
137        for (Callable<T> task : tasks) {
138            Callable<T> work = associateWithSubject(task);
139            workItems.add(work);
140        }
141        return workItems;
142    }
143
144    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
145        Collection<Callable<T>> workItems = associateWithSubject(tasks);
146        return this.targetExecutorService.invokeAll(workItems);
147    }
148
149    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
150            throws InterruptedException {
151        Collection<Callable<T>> workItems = associateWithSubject(tasks);
152        return this.targetExecutorService.invokeAll(workItems, timeout, unit);
153    }
154
155    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
156        Collection<Callable<T>> workItems = associateWithSubject(tasks);
157        return this.targetExecutorService.invokeAny(workItems);
158    }
159
160    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
161            throws InterruptedException, ExecutionException, TimeoutException {
162        Collection<Callable<T>> workItems = associateWithSubject(tasks);
163        return this.targetExecutorService.invokeAny(workItems, timeout, unit);
164    }
165}