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}