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.jca.work; 018 019import java.util.concurrent.Callable; 020import java.util.concurrent.Future; 021import java.util.concurrent.FutureTask; 022import javax.naming.NamingException; 023import javax.resource.spi.BootstrapContext; 024import javax.resource.spi.work.ExecutionContext; 025import javax.resource.spi.work.Work; 026import javax.resource.spi.work.WorkException; 027import javax.resource.spi.work.WorkListener; 028import javax.resource.spi.work.WorkManager; 029import javax.resource.spi.work.WorkRejectedException; 030 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.core.task.AsyncListenableTaskExecutor; 033import org.springframework.core.task.TaskDecorator; 034import org.springframework.core.task.TaskRejectedException; 035import org.springframework.core.task.TaskTimeoutException; 036import org.springframework.jca.context.BootstrapContextAware; 037import org.springframework.jndi.JndiLocatorSupport; 038import org.springframework.scheduling.SchedulingException; 039import org.springframework.scheduling.SchedulingTaskExecutor; 040import org.springframework.util.Assert; 041import org.springframework.util.concurrent.ListenableFuture; 042import org.springframework.util.concurrent.ListenableFutureTask; 043 044/** 045 * {@link org.springframework.core.task.TaskExecutor} implementation 046 * that delegates to a JCA 1.5 WorkManager, implementing the 047 * {@link javax.resource.spi.work.WorkManager} interface. 048 * 049 * <p>This is mainly intended for use within a JCA ResourceAdapter implementation, 050 * but may also be used in a standalone environment, delegating to a locally 051 * embedded WorkManager implementation (such as Geronimo's). 052 * 053 * <p>Also implements the JCA 1.5 WorkManager interface itself, delegating all 054 * calls to the target WorkManager. Hence, a caller can choose whether it wants 055 * to talk to this executor through the Spring TaskExecutor interface or the 056 * JCA 1.5 WorkManager interface. 057 * 058 * <p>This adapter is also capable of obtaining a JCA WorkManager from JNDI. 059 * This is for example appropriate on the Geronimo application server, where 060 * WorkManager GBeans (e.g. Geronimo's default "DefaultWorkManager" GBean) 061 * can be linked into the J2EE environment through "gbean-ref" entries 062 * in the {@code geronimo-web.xml} deployment descriptor. 063 * 064 * <p><b>On JBoss and GlassFish, obtaining the default JCA WorkManager 065 * requires special lookup steps.</b> See the 066 * {@link org.springframework.jca.work.jboss.JBossWorkManagerTaskExecutor} 067 * {@link org.springframework.jca.work.glassfish.GlassFishWorkManagerTaskExecutor} 068 * classes which are the direct equivalent of this generic JCA adapter class. 069 * 070 * @author Juergen Hoeller 071 * @since 2.0.3 072 * @see #setWorkManager 073 * @see javax.resource.spi.work.WorkManager#scheduleWork 074 */ 075public class WorkManagerTaskExecutor extends JndiLocatorSupport 076 implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, WorkManager, BootstrapContextAware, InitializingBean { 077 078 private WorkManager workManager; 079 080 private String workManagerName; 081 082 private boolean blockUntilStarted = false; 083 084 private boolean blockUntilCompleted = false; 085 086 private WorkListener workListener; 087 088 private TaskDecorator taskDecorator; 089 090 091 /** 092 * Create a new WorkManagerTaskExecutor, expecting bean-style configuration. 093 * @see #setWorkManager 094 */ 095 public WorkManagerTaskExecutor() { 096 } 097 098 /** 099 * Create a new WorkManagerTaskExecutor for the given WorkManager. 100 * @param workManager the JCA WorkManager to delegate to 101 */ 102 public WorkManagerTaskExecutor(WorkManager workManager) { 103 setWorkManager(workManager); 104 } 105 106 107 /** 108 * Specify the JCA WorkManager instance to delegate to. 109 */ 110 public void setWorkManager(WorkManager workManager) { 111 Assert.notNull(workManager, "WorkManager must not be null"); 112 this.workManager = workManager; 113 } 114 115 /** 116 * Set the JNDI name of the JCA WorkManager. 117 * <p>This can either be a fully qualified JNDI name, 118 * or the JNDI name relative to the current environment 119 * naming context if "resourceRef" is set to "true". 120 * @see #setWorkManager 121 * @see #setResourceRef 122 */ 123 public void setWorkManagerName(String workManagerName) { 124 this.workManagerName = workManagerName; 125 } 126 127 /** 128 * Specify the JCA BootstrapContext that contains the 129 * WorkManager to delegate to. 130 */ 131 @Override 132 public void setBootstrapContext(BootstrapContext bootstrapContext) { 133 Assert.notNull(bootstrapContext, "BootstrapContext must not be null"); 134 this.workManager = bootstrapContext.getWorkManager(); 135 } 136 137 /** 138 * Set whether to let {@link #execute} block until the work 139 * has been actually started. 140 * <p>Uses the JCA {@code startWork} operation underneath, 141 * instead of the default {@code scheduleWork}. 142 * @see javax.resource.spi.work.WorkManager#startWork 143 * @see javax.resource.spi.work.WorkManager#scheduleWork 144 */ 145 public void setBlockUntilStarted(boolean blockUntilStarted) { 146 this.blockUntilStarted = blockUntilStarted; 147 } 148 149 /** 150 * Set whether to let {@link #execute} block until the work 151 * has been completed. 152 * <p>Uses the JCA {@code doWork} operation underneath, 153 * instead of the default {@code scheduleWork}. 154 * @see javax.resource.spi.work.WorkManager#doWork 155 * @see javax.resource.spi.work.WorkManager#scheduleWork 156 */ 157 public void setBlockUntilCompleted(boolean blockUntilCompleted) { 158 this.blockUntilCompleted = blockUntilCompleted; 159 } 160 161 /** 162 * Specify a JCA 1.5 WorkListener to apply, if any. 163 * <p>This shared WorkListener instance will be passed on to the 164 * WorkManager by all {@link #execute} calls on this TaskExecutor. 165 */ 166 public void setWorkListener(WorkListener workListener) { 167 this.workListener = workListener; 168 } 169 170 /** 171 * Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable} 172 * about to be executed. 173 * <p>Note that such a decorator is not necessarily being applied to the 174 * user-supplied {@code Runnable}/{@code Callable} but rather to the actual 175 * execution callback (which may be a wrapper around the user-supplied task). 176 * <p>The primary use case is to set some execution context around the task's 177 * invocation, or to provide some monitoring/statistics for task execution. 178 * <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations 179 * is limited to plain {@code Runnable} execution via {@code execute} calls. 180 * In case of {@code #submit} calls, the exposed {@code Runnable} will be a 181 * {@code FutureTask} which does not propagate any exceptions; you might 182 * have to cast it and call {@code Future#get} to evaluate exceptions. 183 * @since 4.3 184 */ 185 public void setTaskDecorator(TaskDecorator taskDecorator) { 186 this.taskDecorator = taskDecorator; 187 } 188 189 @Override 190 public void afterPropertiesSet() throws NamingException { 191 if (this.workManager == null) { 192 if (this.workManagerName != null) { 193 this.workManager = lookup(this.workManagerName, WorkManager.class); 194 } 195 else { 196 this.workManager = getDefaultWorkManager(); 197 } 198 } 199 } 200 201 /** 202 * Obtain a default WorkManager to delegate to. 203 * Called if no explicit WorkManager or WorkManager JNDI name has been specified. 204 * <p>The default implementation returns a {@link SimpleTaskWorkManager}. 205 * Can be overridden in subclasses. 206 */ 207 protected WorkManager getDefaultWorkManager() { 208 return new SimpleTaskWorkManager(); 209 } 210 211 212 //------------------------------------------------------------------------- 213 // Implementation of the Spring SchedulingTaskExecutor interface 214 //------------------------------------------------------------------------- 215 216 @Override 217 public void execute(Runnable task) { 218 execute(task, TIMEOUT_INDEFINITE); 219 } 220 221 @Override 222 public void execute(Runnable task, long startTimeout) { 223 Assert.state(this.workManager != null, "No WorkManager specified"); 224 Work work = new DelegatingWork(this.taskDecorator != null ? this.taskDecorator.decorate(task) : task); 225 try { 226 if (this.blockUntilCompleted) { 227 if (startTimeout != TIMEOUT_INDEFINITE || this.workListener != null) { 228 this.workManager.doWork(work, startTimeout, null, this.workListener); 229 } 230 else { 231 this.workManager.doWork(work); 232 } 233 } 234 else if (this.blockUntilStarted) { 235 if (startTimeout != TIMEOUT_INDEFINITE || this.workListener != null) { 236 this.workManager.startWork(work, startTimeout, null, this.workListener); 237 } 238 else { 239 this.workManager.startWork(work); 240 } 241 } 242 else { 243 if (startTimeout != TIMEOUT_INDEFINITE || this.workListener != null) { 244 this.workManager.scheduleWork(work, startTimeout, null, this.workListener); 245 } 246 else { 247 this.workManager.scheduleWork(work); 248 } 249 } 250 } 251 catch (WorkRejectedException ex) { 252 if (WorkException.START_TIMED_OUT.equals(ex.getErrorCode())) { 253 throw new TaskTimeoutException("JCA WorkManager rejected task because of timeout: " + task, ex); 254 } 255 else { 256 throw new TaskRejectedException("JCA WorkManager rejected task: " + task, ex); 257 } 258 } 259 catch (WorkException ex) { 260 throw new SchedulingException("Could not schedule task on JCA WorkManager", ex); 261 } 262 } 263 264 @Override 265 public Future<?> submit(Runnable task) { 266 FutureTask<Object> future = new FutureTask<Object>(task, null); 267 execute(future, TIMEOUT_INDEFINITE); 268 return future; 269 } 270 271 @Override 272 public <T> Future<T> submit(Callable<T> task) { 273 FutureTask<T> future = new FutureTask<T>(task); 274 execute(future, TIMEOUT_INDEFINITE); 275 return future; 276 } 277 278 @Override 279 public ListenableFuture<?> submitListenable(Runnable task) { 280 ListenableFutureTask<Object> future = new ListenableFutureTask<Object>(task, null); 281 execute(future, TIMEOUT_INDEFINITE); 282 return future; 283 } 284 285 @Override 286 public <T> ListenableFuture<T> submitListenable(Callable<T> task) { 287 ListenableFutureTask<T> future = new ListenableFutureTask<T>(task); 288 execute(future, TIMEOUT_INDEFINITE); 289 return future; 290 } 291 292 /** 293 * This task executor prefers short-lived work units. 294 */ 295 @Override 296 public boolean prefersShortLivedTasks() { 297 return true; 298 } 299 300 301 //------------------------------------------------------------------------- 302 // Implementation of the JCA WorkManager interface 303 //------------------------------------------------------------------------- 304 305 @Override 306 public void doWork(Work work) throws WorkException { 307 this.workManager.doWork(work); 308 } 309 310 @Override 311 public void doWork(Work work, long delay, ExecutionContext executionContext, WorkListener workListener) 312 throws WorkException { 313 314 this.workManager.doWork(work, delay, executionContext, workListener); 315 } 316 317 @Override 318 public long startWork(Work work) throws WorkException { 319 return this.workManager.startWork(work); 320 } 321 322 @Override 323 public long startWork(Work work, long delay, ExecutionContext executionContext, WorkListener workListener) 324 throws WorkException { 325 326 return this.workManager.startWork(work, delay, executionContext, workListener); 327 } 328 329 @Override 330 public void scheduleWork(Work work) throws WorkException { 331 this.workManager.scheduleWork(work); 332 } 333 334 @Override 335 public void scheduleWork(Work work, long delay, ExecutionContext executionContext, WorkListener workListener) 336 throws WorkException { 337 338 this.workManager.scheduleWork(work, delay, executionContext, workListener); 339 } 340 341}