001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.scheduling.commonj;
018
019import java.util.Collection;
020import java.util.concurrent.Callable;
021import java.util.concurrent.Future;
022import java.util.concurrent.FutureTask;
023import javax.naming.NamingException;
024
025import commonj.work.Work;
026import commonj.work.WorkException;
027import commonj.work.WorkItem;
028import commonj.work.WorkListener;
029import commonj.work.WorkManager;
030import commonj.work.WorkRejectedException;
031
032import org.springframework.beans.factory.InitializingBean;
033import org.springframework.core.task.AsyncListenableTaskExecutor;
034import org.springframework.core.task.TaskDecorator;
035import org.springframework.core.task.TaskRejectedException;
036import org.springframework.jndi.JndiLocatorSupport;
037import org.springframework.scheduling.SchedulingException;
038import org.springframework.scheduling.SchedulingTaskExecutor;
039import org.springframework.util.Assert;
040import org.springframework.util.concurrent.ListenableFuture;
041import org.springframework.util.concurrent.ListenableFutureTask;
042
043/**
044 * TaskExecutor implementation that delegates to a CommonJ WorkManager,
045 * implementing the {@link commonj.work.WorkManager} interface,
046 * which either needs to be specified as reference or through the JNDI name.
047 *
048 * <p><b>This is the central convenience class for setting up a
049 * CommonJ WorkManager in a Spring context.</b>
050 *
051 * <p>Also implements the CommonJ WorkManager interface itself, delegating all
052 * calls to the target WorkManager. Hence, a caller can choose whether it wants
053 * to talk to this executor through the Spring TaskExecutor interface or the
054 * CommonJ WorkManager interface.
055 *
056 * <p>The CommonJ WorkManager will usually be retrieved from the application
057 * server's JNDI environment, as defined in the server's management console.
058 *
059 * <p>Note: On EE 7/8 compliant versions of WebLogic and WebSphere, a
060 * {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor}
061 * should be preferred, following JSR-236 support in Java EE 7/8.
062 *
063 * @author Juergen Hoeller
064 * @since 2.0
065 */
066public class WorkManagerTaskExecutor extends JndiLocatorSupport
067                implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, WorkManager, InitializingBean {
068
069        private WorkManager workManager;
070
071        private String workManagerName;
072
073        private WorkListener workListener;
074
075        private TaskDecorator taskDecorator;
076
077
078        /**
079         * Specify the CommonJ WorkManager to delegate to.
080         * <p>Alternatively, you can also specify the JNDI name of the target WorkManager.
081         * @see #setWorkManagerName
082         */
083        public void setWorkManager(WorkManager workManager) {
084                this.workManager = workManager;
085        }
086
087        /**
088         * Set the JNDI name of the CommonJ WorkManager.
089         * <p>This can either be a fully qualified JNDI name, or the JNDI name relative
090         * to the current environment naming context if "resourceRef" is set to "true".
091         * @see #setWorkManager
092         * @see #setResourceRef
093         */
094        public void setWorkManagerName(String workManagerName) {
095                this.workManagerName = workManagerName;
096        }
097
098        /**
099         * Specify a CommonJ WorkListener to apply, if any.
100         * <p>This shared WorkListener instance will be passed on to the
101         * WorkManager by all {@link #execute} calls on this TaskExecutor.
102         */
103        public void setWorkListener(WorkListener workListener) {
104                this.workListener = workListener;
105        }
106
107        /**
108         * Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable}
109         * about to be executed.
110         * <p>Note that such a decorator is not necessarily being applied to the
111         * user-supplied {@code Runnable}/{@code Callable} but rather to the actual
112         * execution callback (which may be a wrapper around the user-supplied task).
113         * <p>The primary use case is to set some execution context around the task's
114         * invocation, or to provide some monitoring/statistics for task execution.
115         * <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
116         * is limited to plain {@code Runnable} execution via {@code execute} calls.
117         * In case of {@code #submit} calls, the exposed {@code Runnable} will be a
118         * {@code FutureTask} which does not propagate any exceptions; you might
119         * have to cast it and call {@code Future#get} to evaluate exceptions.
120         * @since 4.3
121         */
122        public void setTaskDecorator(TaskDecorator taskDecorator) {
123                this.taskDecorator = taskDecorator;
124        }
125
126        @Override
127        public void afterPropertiesSet() throws NamingException {
128                if (this.workManager == null) {
129                        if (this.workManagerName == null) {
130                                throw new IllegalArgumentException("Either 'workManager' or 'workManagerName' must be specified");
131                        }
132                        this.workManager = lookup(this.workManagerName, WorkManager.class);
133                }
134        }
135
136
137        //-------------------------------------------------------------------------
138        // Implementation of the Spring SchedulingTaskExecutor interface
139        //-------------------------------------------------------------------------
140
141        @Override
142        public void execute(Runnable task) {
143                Assert.state(this.workManager != null, "No WorkManager specified");
144                Work work = new DelegatingWork(this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
145                try {
146                        if (this.workListener != null) {
147                                this.workManager.schedule(work, this.workListener);
148                        }
149                        else {
150                                this.workManager.schedule(work);
151                        }
152                }
153                catch (WorkRejectedException ex) {
154                        throw new TaskRejectedException("CommonJ WorkManager did not accept task: " + task, ex);
155                }
156                catch (WorkException ex) {
157                        throw new SchedulingException("Could not schedule task on CommonJ WorkManager", ex);
158                }
159        }
160
161        @Override
162        public void execute(Runnable task, long startTimeout) {
163                execute(task);
164        }
165
166        @Override
167        public Future<?> submit(Runnable task) {
168                FutureTask<Object> future = new FutureTask<Object>(task, null);
169                execute(future);
170                return future;
171        }
172
173        @Override
174        public <T> Future<T> submit(Callable<T> task) {
175                FutureTask<T> future = new FutureTask<T>(task);
176                execute(future);
177                return future;
178        }
179
180        @Override
181        public ListenableFuture<?> submitListenable(Runnable task) {
182                ListenableFutureTask<Object> future = new ListenableFutureTask<Object>(task, null);
183                execute(future);
184                return future;
185        }
186
187        @Override
188        public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
189                ListenableFutureTask<T> future = new ListenableFutureTask<T>(task);
190                execute(future);
191                return future;
192        }
193
194        /**
195         * This task executor prefers short-lived work units.
196         */
197        @Override
198        public boolean prefersShortLivedTasks() {
199                return true;
200        }
201
202
203        //-------------------------------------------------------------------------
204        // Implementation of the CommonJ WorkManager interface
205        //-------------------------------------------------------------------------
206
207        @Override
208        public WorkItem schedule(Work work) throws WorkException, IllegalArgumentException {
209                return this.workManager.schedule(work);
210        }
211
212        @Override
213        public WorkItem schedule(Work work, WorkListener workListener) throws WorkException {
214                return this.workManager.schedule(work, workListener);
215        }
216
217        @Override
218        @SuppressWarnings("rawtypes")
219        public boolean waitForAll(Collection workItems, long timeout) throws InterruptedException {
220                return this.workManager.waitForAll(workItems, timeout);
221        }
222
223        @Override
224        @SuppressWarnings("rawtypes")
225        public Collection waitForAny(Collection workItems, long timeout) throws InterruptedException {
226                return this.workManager.waitForAny(workItems, timeout);
227        }
228
229}