001/* 002 * Copyright 2002-2013 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.config; 018 019import org.w3c.dom.Element; 020import org.w3c.dom.Node; 021import org.w3c.dom.NodeList; 022 023import org.springframework.beans.factory.config.RuntimeBeanReference; 024import org.springframework.beans.factory.parsing.BeanComponentDefinition; 025import org.springframework.beans.factory.support.BeanDefinitionBuilder; 026import org.springframework.beans.factory.support.ManagedList; 027import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; 028import org.springframework.beans.factory.xml.ParserContext; 029import org.springframework.util.StringUtils; 030 031/** 032 * Parser for the 'scheduled-tasks' element of the scheduling namespace. 033 * 034 * @author Mark Fisher 035 * @author Chris Beams 036 * @since 3.0 037 */ 038public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 039 040 private static final String ELEMENT_SCHEDULED = "scheduled"; 041 042 private static final long ZERO_INITIAL_DELAY = 0; 043 044 045 @Override 046 protected boolean shouldGenerateId() { 047 return true; 048 } 049 050 @Override 051 protected String getBeanClassName(Element element) { 052 return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar"; 053 } 054 055 @Override 056 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { 057 builder.setLazyInit(false); // lazy scheduled tasks are a contradiction in terms -> force to false 058 ManagedList<RuntimeBeanReference> cronTaskList = new ManagedList<RuntimeBeanReference>(); 059 ManagedList<RuntimeBeanReference> fixedDelayTaskList = new ManagedList<RuntimeBeanReference>(); 060 ManagedList<RuntimeBeanReference> fixedRateTaskList = new ManagedList<RuntimeBeanReference>(); 061 ManagedList<RuntimeBeanReference> triggerTaskList = new ManagedList<RuntimeBeanReference>(); 062 NodeList childNodes = element.getChildNodes(); 063 for (int i = 0; i < childNodes.getLength(); i++) { 064 Node child = childNodes.item(i); 065 if (!isScheduledElement(child, parserContext)) { 066 continue; 067 } 068 Element taskElement = (Element) child; 069 String ref = taskElement.getAttribute("ref"); 070 String method = taskElement.getAttribute("method"); 071 072 // Check that 'ref' and 'method' are specified 073 if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) { 074 parserContext.getReaderContext().error("Both 'ref' and 'method' are required", taskElement); 075 // Continue with the possible next task element 076 continue; 077 } 078 079 String cronAttribute = taskElement.getAttribute("cron"); 080 String fixedDelayAttribute = taskElement.getAttribute("fixed-delay"); 081 String fixedRateAttribute = taskElement.getAttribute("fixed-rate"); 082 String triggerAttribute = taskElement.getAttribute("trigger"); 083 String initialDelayAttribute = taskElement.getAttribute("initial-delay"); 084 085 boolean hasCronAttribute = StringUtils.hasText(cronAttribute); 086 boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute); 087 boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute); 088 boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute); 089 boolean hasInitialDelayAttribute = StringUtils.hasText(initialDelayAttribute); 090 091 if (!(hasCronAttribute || hasFixedDelayAttribute || hasFixedRateAttribute || hasTriggerAttribute)) { 092 parserContext.getReaderContext().error( 093 "one of the 'cron', 'fixed-delay', 'fixed-rate', or 'trigger' attributes is required", taskElement); 094 continue; // with the possible next task element 095 } 096 097 if (hasInitialDelayAttribute && (hasCronAttribute || hasTriggerAttribute)) { 098 parserContext.getReaderContext().error( 099 "the 'initial-delay' attribute may not be used with cron and trigger tasks", taskElement); 100 continue; // with the possible next task element 101 } 102 103 String runnableName = 104 runnableReference(ref, method, taskElement, parserContext).getBeanName(); 105 106 if (hasFixedDelayAttribute) { 107 fixedDelayTaskList.add(intervalTaskReference(runnableName, 108 initialDelayAttribute, fixedDelayAttribute, taskElement, parserContext)); 109 } 110 if (hasFixedRateAttribute) { 111 fixedRateTaskList.add(intervalTaskReference(runnableName, 112 initialDelayAttribute, fixedRateAttribute, taskElement, parserContext)); 113 } 114 if (hasCronAttribute) { 115 cronTaskList.add(cronTaskReference(runnableName, cronAttribute, 116 taskElement, parserContext)); 117 } 118 if (hasTriggerAttribute) { 119 String triggerName = new RuntimeBeanReference(triggerAttribute).getBeanName(); 120 triggerTaskList.add(triggerTaskReference(runnableName, triggerName, 121 taskElement, parserContext)); 122 } 123 } 124 String schedulerRef = element.getAttribute("scheduler"); 125 if (StringUtils.hasText(schedulerRef)) { 126 builder.addPropertyReference("taskScheduler", schedulerRef); 127 } 128 builder.addPropertyValue("cronTasksList", cronTaskList); 129 builder.addPropertyValue("fixedDelayTasksList", fixedDelayTaskList); 130 builder.addPropertyValue("fixedRateTasksList", fixedRateTaskList); 131 builder.addPropertyValue("triggerTasksList", triggerTaskList); 132 } 133 134 private boolean isScheduledElement(Node node, ParserContext parserContext) { 135 return node.getNodeType() == Node.ELEMENT_NODE && 136 ELEMENT_SCHEDULED.equals(parserContext.getDelegate().getLocalName(node)); 137 } 138 139 private RuntimeBeanReference runnableReference(String ref, String method, Element taskElement, ParserContext parserContext) { 140 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( 141 "org.springframework.scheduling.support.ScheduledMethodRunnable"); 142 builder.addConstructorArgReference(ref); 143 builder.addConstructorArgValue(method); 144 return beanReference(taskElement, parserContext, builder); 145 } 146 147 private RuntimeBeanReference intervalTaskReference(String runnableBeanName, 148 String initialDelay, String interval, Element taskElement, ParserContext parserContext) { 149 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( 150 "org.springframework.scheduling.config.IntervalTask"); 151 builder.addConstructorArgReference(runnableBeanName); 152 builder.addConstructorArgValue(interval); 153 builder.addConstructorArgValue(StringUtils.hasLength(initialDelay) ? initialDelay : ZERO_INITIAL_DELAY); 154 return beanReference(taskElement, parserContext, builder); 155 } 156 157 private RuntimeBeanReference cronTaskReference(String runnableBeanName, 158 String cronExpression, Element taskElement, ParserContext parserContext) { 159 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( 160 "org.springframework.scheduling.config.CronTask"); 161 builder.addConstructorArgReference(runnableBeanName); 162 builder.addConstructorArgValue(cronExpression); 163 return beanReference(taskElement, parserContext, builder); 164 } 165 166 private RuntimeBeanReference triggerTaskReference(String runnableBeanName, 167 String triggerBeanName, Element taskElement, ParserContext parserContext) { 168 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( 169 "org.springframework.scheduling.config.TriggerTask"); 170 builder.addConstructorArgReference(runnableBeanName); 171 builder.addConstructorArgReference(triggerBeanName); 172 return beanReference(taskElement, parserContext, builder); 173 } 174 175 private RuntimeBeanReference beanReference(Element taskElement, 176 ParserContext parserContext, BeanDefinitionBuilder builder) { 177 // Extract the source of the current task 178 builder.getRawBeanDefinition().setSource(parserContext.extractSource(taskElement)); 179 String generatedName = parserContext.getReaderContext().generateBeanName(builder.getRawBeanDefinition()); 180 parserContext.registerBeanComponent(new BeanComponentDefinition(builder.getBeanDefinition(), generatedName)); 181 return new RuntimeBeanReference(generatedName); 182 } 183 184}