diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 74ced65a7ea..9f638cc0f03 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.scheduling.config.ScheduledTaskRegistrar; -import org.springframework.scheduling.support.MethodInvokingRunnable; +import org.springframework.scheduling.support.ScheduledMethodRunnable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; @@ -103,16 +103,7 @@ public class ScheduledAnnotationBeanPostProcessor "Only void-returning methods may be annotated with @Scheduled."); Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with @Scheduled."); - MethodInvokingRunnable runnable = new MethodInvokingRunnable(); - runnable.setTargetObject(bean); - runnable.setTargetMethod(method.getName()); - runnable.setArguments(new Object[0]); - try { - runnable.prepare(); - } - catch (Exception ex) { - throw new IllegalStateException("failed to prepare task", ex); - } + Runnable runnable = new ScheduledMethodRunnable(bean, method); boolean processedSchedule = false; String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required."; String cron = annotation.cron(); diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java index 01f575fa3ab..9a75475ec1c 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java @@ -16,6 +16,10 @@ package org.springframework.scheduling.config; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -23,9 +27,6 @@ import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; /** * Parser for the 'scheduled-tasks' element of the scheduling namespace. @@ -70,7 +71,7 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini } RuntimeBeanReference runnableBeanRef = new RuntimeBeanReference( - this.createRunnableBean(ref, method, taskElement, parserContext)); + createRunnableBean(ref, method, taskElement, parserContext)); String cronAttribute = taskElement.getAttribute("cron"); if (StringUtils.hasText(cronAttribute)) { cronTaskMap.put(runnableBeanRef, cronAttribute); @@ -108,9 +109,9 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini private String createRunnableBean(String ref, String method, Element taskElement, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.scheduling.support.MethodInvokingRunnable"); - builder.addPropertyReference("targetObject", ref); - builder.addPropertyValue("targetMethod", method); + "org.springframework.scheduling.support.ScheduledMethodRunnable"); + builder.addConstructorArgReference(ref); + builder.addConstructorArgValue(method); // Extract the source of the current task builder.getRawBeanDefinition().setSource(parserContext.extractSource(taskElement)); String generatedName = parserContext.getReaderContext().generateBeanName(builder.getRawBeanDefinition()); diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/support/DelegatingErrorHandlingRunnable.java b/org.springframework.context/src/main/java/org/springframework/scheduling/support/DelegatingErrorHandlingRunnable.java index 4702b73986f..34cd55286eb 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/support/DelegatingErrorHandlingRunnable.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/support/DelegatingErrorHandlingRunnable.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.scheduling.support; +import java.lang.reflect.UndeclaredThrowableException; + import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; @@ -50,6 +52,9 @@ public class DelegatingErrorHandlingRunnable implements Runnable { try { this.delegate.run(); } + catch (UndeclaredThrowableException ex) { + this.errorHandler.handleError(ex.getUndeclaredThrowable()); + } catch (Throwable ex) { this.errorHandler.handleError(ex); } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/support/ScheduledMethodRunnable.java b/org.springframework.context/src/main/java/org/springframework/scheduling/support/ScheduledMethodRunnable.java new file mode 100644 index 00000000000..2f7270747e6 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/support/ScheduledMethodRunnable.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.support; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; + +import org.springframework.util.ReflectionUtils; + +/** + * Variation of {@link MethodInvokingRunnable} meant to be used for processing + * of no-arg scheduled methods. Propagates user exceptions to the caller, + * assuming that an error strategy for Runnables is in place. + * + * @author Juergen Hoeller + * @since 3.0.6 + */ +public class ScheduledMethodRunnable implements Runnable { + + private final Object target; + + private final Method method; + + + public ScheduledMethodRunnable(Object target, Method method) { + this.target = target; + this.method = method; + } + + public ScheduledMethodRunnable(Object target, String methodName) throws NoSuchMethodException { + this.target = target; + this.method = target.getClass().getMethod(methodName); + } + + + public Object getTarget() { + return this.target; + } + + public Method getMethod() { + return this.method; + } + + + public void run() { + try { + this.method.invoke(this.target); + } + catch (InvocationTargetException ex) { + ReflectionUtils.rethrowRuntimeException(ex.getTargetException()); + } + catch (IllegalAccessException ex) { + throw new UndeclaredThrowableException(ex); + } + } + +} diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java index 47e59a6357f..f1ffde04117 100644 --- a/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java +++ b/org.springframework.context/src/test/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,16 @@ package org.springframework.scheduling.annotation; -import static org.junit.Assert.assertEquals; - +import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Method; import java.util.Map; import java.util.Properties; +import static org.junit.Assert.*; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; @@ -34,12 +35,12 @@ import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.StaticApplicationContext; import org.springframework.scheduling.config.ScheduledTaskRegistrar; -import org.springframework.scheduling.support.MethodInvokingRunnable; +import org.springframework.scheduling.support.ScheduledMethodRunnable; /** * @author Mark Fisher + * @author Juergen Hoeller */ -@SuppressWarnings({"unchecked", "unused"}) public class ScheduledAnnotationBeanPostProcessorTests { @Test @@ -58,11 +59,11 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map fixedDelayTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("fixedDelayTasks"); assertEquals(1, fixedDelayTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) fixedDelayTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedDelayTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("fixedDelay", targetMethod); + assertEquals("fixedDelay", targetMethod.getName()); assertEquals(new Long(5000), fixedDelayTasks.values().iterator().next()); } @@ -82,16 +83,16 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map fixedRateTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks"); assertEquals(1, fixedRateTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) fixedRateTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedRateTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("fixedRate", targetMethod); + assertEquals("fixedRate", targetMethod.getName()); assertEquals(new Long(3000), fixedRateTasks.values().iterator().next()); } @Test - public void cronTask() { + public void cronTask() throws InterruptedException { StaticApplicationContext context = new StaticApplicationContext(); BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class); BeanDefinition targetDefinition = new RootBeanDefinition( @@ -106,12 +107,13 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map cronTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("cronTasks"); assertEquals(1, cronTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) cronTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("cron", targetMethod); + assertEquals("cron", targetMethod.getName()); assertEquals("*/7 * * * * ?", cronTasks.values().iterator().next()); + Thread.sleep(10000); } @Test @@ -130,11 +132,11 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map fixedRateTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("fixedRateTasks"); assertEquals(1, fixedRateTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) fixedRateTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) fixedRateTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("checkForUpdates", targetMethod); + assertEquals("checkForUpdates", targetMethod.getName()); assertEquals(new Long(5000), fixedRateTasks.values().iterator().next()); } @@ -154,11 +156,11 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map cronTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("cronTasks"); assertEquals(1, cronTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) cronTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("generateReport", targetMethod); + assertEquals("generateReport", targetMethod.getName()); assertEquals("0 0 * * * ?", cronTasks.values().iterator().next()); } @@ -184,11 +186,11 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map cronTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("cronTasks"); assertEquals(1, cronTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) cronTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("x", targetMethod); + assertEquals("x", targetMethod.getName()); assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next()); } @@ -214,11 +216,11 @@ public class ScheduledAnnotationBeanPostProcessorTests { Map cronTasks = (Map) new DirectFieldAccessor(registrar).getPropertyValue("cronTasks"); assertEquals(1, cronTasks.size()); - MethodInvokingRunnable runnable = (MethodInvokingRunnable) cronTasks.keySet().iterator().next(); - Object targetObject = runnable.getTargetObject(); - String targetMethod = runnable.getTargetMethod(); + ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) cronTasks.keySet().iterator().next(); + Object targetObject = runnable.getTarget(); + Method targetMethod = runnable.getMethod(); assertEquals(target, targetObject); - assertEquals("y", targetMethod); + assertEquals("y", targetMethod.getName()); assertEquals(businessHoursCronExpression, cronTasks.values().iterator().next()); } @@ -267,28 +269,27 @@ public class ScheduledAnnotationBeanPostProcessorTests { } - private static class FixedDelayTestBean { + public static class FixedDelayTestBean { @Scheduled(fixedDelay=5000) public void fixedDelay() { } - } - private static class FixedRateTestBean { + public static class FixedRateTestBean { @Scheduled(fixedRate=3000) public void fixedRate() { } - } - private static class CronTestBean { + public static class CronTestBean { @Scheduled(cron="*/7 * * * * ?") - public void cron() { + public void cron() throws IOException { + throw new IOException("no no no"); } } diff --git a/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java index c9588608faa..9f2127ad116 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.UndeclaredThrowableException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -251,7 +252,7 @@ public abstract class ReflectionUtils { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } - handleUnexpectedException(ex); + throw new UndeclaredThrowableException(ex); } /** @@ -283,7 +284,7 @@ public abstract class ReflectionUtils { if (ex instanceof Error) { throw (Error) ex; } - handleUnexpectedException(ex); + throw new UndeclaredThrowableException(ex); } /** @@ -304,15 +305,7 @@ public abstract class ReflectionUtils { if (ex instanceof Error) { throw (Error) ex; } - handleUnexpectedException(ex); - } - - /** - * Throws an IllegalStateException with the given exception as root cause. - * @param ex the unexpected exception - */ - private static void handleUnexpectedException(Throwable ex) { - throw new IllegalStateException("Unexpected exception thrown", ex); + throw new UndeclaredThrowableException(ex); } /**