diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractScriptParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractScriptParser.java new file mode 100644 index 0000000000..4f527ac6fe --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractScriptParser.java @@ -0,0 +1,132 @@ +/* + * 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.integration.config.xml; + +import java.util.List; + +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.beans.factory.xml.XmlReaderContext; +import org.springframework.scripting.support.StaticScriptSource; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * @author David Turanski + * + */ +public abstract class AbstractScriptParser extends AbstractSingleBeanDefinitionParser { + protected static final String LOCATION_ATTRIBUTE = "location"; + + protected static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay"; + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + protected abstract String getBeanClassName(Element element); + + protected abstract String getScriptSourceClassName(); + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String scriptLocation = element.getAttribute(LOCATION_ATTRIBUTE); + String scriptText = DomUtils.getTextValue(element); + if (!(StringUtils.hasText(scriptLocation) ^ StringUtils.hasText(scriptText))) { + parserContext.getReaderContext().error("Either the 'location' attribute or inline script text must be provided, but not both.", element); + return; + } + + List variableElements = DomUtils.getChildElementsByTagName(element, "variable"); + String scriptVariableGeneratorName = element.getAttribute("script-variable-generator"); + + if (StringUtils.hasText(scriptText) && (variableElements.size() > 0 || StringUtils.hasText(scriptVariableGeneratorName))) { + parserContext.getReaderContext().error("Variable bindings or custom ScriptVariableGenerator are not allowed when using an inline groovy script. " + + "Specify location of the script via 'location' attribute instead", element); + return; + } + + if (StringUtils.hasText(scriptVariableGeneratorName) && variableElements.size() > 0) { + parserContext.getReaderContext().error("'script-variable-generator' and 'variable' sub-elements are mutually exclusive.", element); + return; + } + + if (StringUtils.hasText(scriptLocation)) { + builder.addConstructorArgValue(this.resolveScriptLocation(element, parserContext.getReaderContext(), scriptLocation)); + } + else { + if (getScriptSourceClassName() != null){ + builder.addConstructorArgValue(new StaticScriptSource(scriptText, getScriptSourceClassName())); + } else { + builder.addConstructorArgValue(new StaticScriptSource(scriptText)); + } + } + if (!StringUtils.hasText(scriptVariableGeneratorName)) { + BeanDefinitionBuilder scriptVariableGeneratorBuilder = + BeanDefinitionBuilder.genericBeanDefinition("org.springframework.integration.scripting.DefaultScriptVariableGenerator"); + ManagedMap variableMap = new ManagedMap(); + for (Element childElement : variableElements) { + String variableName = childElement.getAttribute("name"); + String variableValue = childElement.getAttribute("value"); + String variableRef = childElement.getAttribute("ref"); + if (!(StringUtils.hasText(variableValue) ^ StringUtils.hasText(variableRef))) { + parserContext.getReaderContext().error("Exactly one of the 'ref' attribute or 'value' attribute, " + + " is required for element " + + IntegrationNamespaceUtils.createElementDescription(element) + ".", element); + } + if (StringUtils.hasText(variableValue)) { + variableMap.put(variableName, variableValue); + } + else { + variableMap.put(variableName, new RuntimeBeanReference(variableRef)); + } + } + if (!CollectionUtils.isEmpty(variableMap)) { + scriptVariableGeneratorBuilder.addConstructorArgValue(variableMap); + } + scriptVariableGeneratorName = BeanDefinitionReaderUtils.registerWithGeneratedName( + scriptVariableGeneratorBuilder.getBeanDefinition(), parserContext.getRegistry()); + } + builder.addConstructorArgReference(scriptVariableGeneratorName); + postProcess(builder, element, parserContext); + } + + /** + * Subclasses may override this no-op method to provide additional configuration. + */ + protected void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + } + + private Object resolveScriptLocation(Element element, XmlReaderContext readerContext, String scriptLocation) { + String refreshDelayText = element.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); + String beanClassName = "org.springframework.integration.scripting.RefreshableResourceScriptSource"; + BeanDefinitionBuilder resourceScriptSourceBuilder = + BeanDefinitionBuilder.genericBeanDefinition(beanClassName); + resourceScriptSourceBuilder.addConstructorArgValue(scriptLocation); + if (StringUtils.hasText(refreshDelayText)) { + resourceScriptSourceBuilder.addConstructorArgValue(refreshDelayText); + } + else { + resourceScriptSourceBuilder.addConstructorArgValue(-1L); + } + return resourceScriptSourceBuilder.getBeanDefinition(); + } + + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java b/spring-integration-core/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java new file mode 100644 index 0000000000..3f928ed35b --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2010 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.integration.scripting; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import org.springframework.core.io.Resource; +import org.springframework.scripting.ScriptSource; +import org.springframework.scripting.support.ResourceScriptSource; + +/** + * @author Dave Syer + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class RefreshableResourceScriptSource implements ScriptSource { + + private final long refreshDelay; + + private final ResourceScriptSource source; + + private final AtomicLong lastModifiedChecked = new AtomicLong(System.currentTimeMillis()); + + private volatile String script; + + + public RefreshableResourceScriptSource(Resource resource, long refreshDelay) { + this.refreshDelay = refreshDelay; + this.source = new ResourceScriptSource(resource); + try { + this.script = this.source.getScriptAsString(); + } + catch (IOException e) { + this.lastModifiedChecked.set(0); + } + } + + public String getScriptAsString() throws IOException { + this.script = source.getScriptAsString(); + return this.script; + } + + public String suggestedClassName() { + return this.source.suggestedClassName(); + } + + public boolean isModified() { + if (this.refreshDelay < 0) { + return false; + } + long time = System.currentTimeMillis(); + if (this.refreshDelay == 0 || (time - this.lastModifiedChecked.get()) > this.refreshDelay) { + this.lastModifiedChecked.set(time); + return this.source.isModified(); + } + return false; + } + +} diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/scripting/config/xml/spring-integration-scripting-core-2.1.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/scripting/config/xml/spring-integration-scripting-core-2.1.xsd new file mode 100644 index 0000000000..a509933760 --- /dev/null +++ b/spring-integration-core/src/main/resources/org/springframework/integration/scripting/config/xml/spring-integration-scripting-core-2.1.xsd @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + Allows you to define custom script variable bindings. The use of this + sub-element is mutually + exclusive with the 'script-variable-generator' attribute. + + + + + + Name of the script variable. + + + + + + + Value of the script variable as a literal. + + + + + + + Value of the script variable as a bean reference. + + + + + + + + + + + + + Resource location path for the Script. Either this or an inline script + as body text should be provided, but not both. + + + + + + + Reference to the ScriptVariableGenerator bean. This attribute is mutually + exclusive with any 'variable' sub-elements. + + + + + + + + + + + Refresh delay for the script contents if specified as a resource + location (defaults to -1, never refresh). + + + + + + diff --git a/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.1.xsd b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.1.xsd new file mode 100644 index 0000000000..5d368bc0ba --- /dev/null +++ b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.1.xsd @@ -0,0 +1,72 @@ + + + + + + + + + + + Configures an inner bean that will generate a + Groovy Script. + + + + + + + + + + + Reference to a GroovyObjectCustomizer bean to be applied to this script. + + + + + + + + + + + + + + + + + + + + + + + + Reference to a GroovyObjectCustomizer bean to be + applied to each script payload. + + + + + + + + + +