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    }