INT-1552 doc polishing, also updated Groovy-based Control Bus to be more consistent with the SpEL version

This commit is contained in:
Mark Fisher
2010-11-22 14:01:39 -05:00
parent 36e43a8ed1
commit 4e8076fd2b
8 changed files with 103 additions and 211 deletions

View File

@@ -1,56 +0,0 @@
/*
* 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.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyObject;
import groovy.lang.Script;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
/**
* @author Dave Syer
* @since 2.0
*/
public class BeanFactoryContextBindingCustomizer implements GroovyObjectCustomizer, BeanFactoryAware {
private ListableBeanFactory beanFactory;
public BeanFactoryContextBindingCustomizer() {
this(null);
}
public BeanFactoryContextBindingCustomizer(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (beanFactory instanceof ListableBeanFactory) ? (ListableBeanFactory) beanFactory : null;
}
public void customize(GroovyObject goo) {
if (this.beanFactory != null) {
Binding binding = ((Script) goo).getBinding();
for (String name : this.beanFactory.getBeanDefinitionNames()) {
binding.setVariable(name, this.beanFactory.getBean(name));
}
}
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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.groovy;
import groovy.lang.GString;
import java.util.Map;
import org.springframework.integration.Message;
import org.springframework.integration.handler.AbstractScriptExecutingMessageProcessor;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
import org.springframework.scripting.groovy.GroovyScriptFactory;
import org.springframework.scripting.support.StaticScriptSource;
import org.springframework.util.Assert;
/**
* @author Dave Syer
* @author Mark Fisher
* @since 2.0
*/
public class GroovyScriptPayloadMessageProcessor extends AbstractScriptExecutingMessageProcessor<Object> {
private final GroovyObjectCustomizer customizer;
public GroovyScriptPayloadMessageProcessor() {
this((GroovyObjectCustomizer) null);
}
public GroovyScriptPayloadMessageProcessor(Map<String, ?> map) {
this(new MapContextBindingCustomizer(map));
}
public GroovyScriptPayloadMessageProcessor(GroovyObjectCustomizer customizer) {
this.customizer = customizer;
}
@Override
protected ScriptSource getScriptSource(Message<?> message) {
Object payload = message.getPayload();
Assert.isInstanceOf(String.class, payload, "Payload must be a String containing a Groovy script.");
String className = generateScriptName(message);
return new StaticScriptSource((String) payload, className);
}
@Override
protected Object executeScript(ScriptSource scriptSource, Message<?> message) throws Exception {
// Keeping everything local prevents PermGen (class instances) leaks...
MessageContextBindingCustomizer bindingCustomizer = new MessageContextBindingCustomizer(this.customizer);
bindingCustomizer.setMessage(message);
GroovyScriptFactory scriptFactory = new GroovyScriptFactory(this.getClass().getSimpleName(), bindingCustomizer);
Object result = scriptFactory.getScriptedObject(scriptSource, null);
return (result instanceof GString) ? result.toString() : result;
}
protected String generateScriptName(Message<?> message) {
// Don't use the same script (class) name for all invocations by default
return getClass().getSimpleName() + message.getHeaders().getId().toString().replaceAll("-", "");
}
}

View File

@@ -13,10 +13,21 @@
package org.springframework.integration.groovy.config;
import groovy.lang.Binding;
import groovy.lang.GroovyObject;
import groovy.lang.Script;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.Lifecycle;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor;
import org.springframework.integration.groovy.GroovyCommandMessageProcessor;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
import org.springframework.util.CustomizableThreadCreator;
/**
* FactoryBean for creating {@link MessageHandler} instances to handle a message as a Groovy Script.
@@ -28,13 +39,6 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
private volatile Long sendTimeout;
private final GroovyScriptPayloadMessageProcessor processor;
public GroovyControlBusFactoryBean(GroovyScriptPayloadMessageProcessor processor) {
this.processor = processor;
}
public void setSendTimeout(Long sendTimeout) {
this.sendTimeout = sendTimeout;
@@ -42,7 +46,9 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
@Override
protected MessageHandler createHandler() {
return this.configureHandler(new ServiceActivatingHandler(this.processor));
ControlBusContextBindingCustomizer customizer = new ControlBusContextBindingCustomizer(this.getBeanFactory());
GroovyCommandMessageProcessor processor = new GroovyCommandMessageProcessor(customizer);
return this.configureHandler(new ServiceActivatingHandler(processor));
}
private ServiceActivatingHandler configureHandler(ServiceActivatingHandler handler) {
@@ -52,4 +58,31 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
return handler;
}
/**
* Adds any @ManagedResource, Lifecycle, or CustomizableThreadCreator instances from
* the application context to the Script variable bindings.
*/
private static class ControlBusContextBindingCustomizer implements GroovyObjectCustomizer {
private final ListableBeanFactory beanFactory;
public ControlBusContextBindingCustomizer(BeanFactory beanFactory) {
this.beanFactory = (beanFactory instanceof ListableBeanFactory) ? (ListableBeanFactory) beanFactory : null;
}
public void customize(GroovyObject goo) {
if (this.beanFactory != null) {
Binding binding = ((Script) goo).getBinding();
for (String name : this.beanFactory.getBeanDefinitionNames()) {
Object bean = this.beanFactory.getBean(name);
if (bean instanceof Lifecycle || bean instanceof CustomizableThreadCreator
|| (AnnotationUtils.findAnnotation(bean.getClass(), ManagedResource.class) != null)) {
binding.setVariable(name, bean);
}
}
}
}
}
}

View File

@@ -15,13 +15,10 @@ package org.springframework.integration.groovy.config;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.StringUtils;
/**
* @author Dave Syer
@@ -29,29 +26,12 @@ import org.springframework.util.StringUtils;
*/
public class GroovyControlBusParser extends AbstractConsumerEndpointParser {
private static final String CUSTOMIZER_ATTRIBUTE = "customizer";
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GroovyControlBusFactoryBean.class);
builder.addConstructorArgValue(getMessageProcessorBeanDefinition(element, parserContext));
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "order");
return builder;
}
protected BeanMetadataElement getMessageProcessorBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor");
String customizerAttr = element.getAttribute(CUSTOMIZER_ATTRIBUTE);
if (StringUtils.hasText(customizerAttr)) {
builder.addConstructorArgReference(customizerAttr.trim());
}
else {
builder.addConstructorArgValue(new RootBeanDefinition(
"org.springframework.integration.groovy.BeanFactoryContextBindingCustomizer"));
}
return builder.getBeanDefinition();
}
}

View File

@@ -41,31 +41,17 @@
<xsd:element name="control-bus">
<xsd:annotation>
<xsd:documentation>
<xsd:documentation><![CDATA[
Control bus that accepts messages in the form of Groovy scripts. The scripts should be provided as
String payloads
in incoming messages
</xsd:documentation>
String payload in incoming messages. The variable bindings will include any @ManagedResource, Lifecycle,
or CustomizableThreadCreator instances from within the ApplicationContext.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:element name="poller" type="integration:innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="integration:inputOutputChannelGroup" />
<xsd:attribute name="customizer" use="optional">
<xsd:annotation>
<xsd:documentation>
A reference to a static GroovyObjectCustomizer that will be used to modify the Groovy scripts
sent to the control channel. By default a customizer is used that simply exposes all beans in the application
context by name in the scripts.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="org.springframework.scripting.groovy.GroovyObjectCustomizer" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@@ -36,7 +36,7 @@ public class GroovyScriptPayloadMessageProcessorTests {
private AtomicInteger countHolder = new AtomicInteger();
private GroovyScriptPayloadMessageProcessor processor = new GroovyScriptPayloadMessageProcessor();
private GroovyCommandMessageProcessor processor = new GroovyCommandMessageProcessor();
@Test
@Repeat(20)
@@ -61,7 +61,7 @@ public class GroovyScriptPayloadMessageProcessorTests {
public void testSimpleExecutionWithContext() throws Exception {
Message<?> message = MessageBuilder.withPayload("\"spam is $spam foo is $headers.foo\"")
.setHeader("foo", "bar").build();
MessageProcessor<Object> processor = new GroovyScriptPayloadMessageProcessor(Collections.singletonMap("spam",
MessageProcessor<Object> processor = new GroovyCommandMessageProcessor(Collections.singletonMap("spam",
"bucket"));
Object result = processor.processMessage(message);
assertEquals("spam is bucket foo is bar", result.toString());

View File

@@ -21,11 +21,14 @@ import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -51,9 +54,13 @@ public class GroovyControlBusTests {
assertNull(output.receive(0));
}
@ManagedResource
public static class Service {
@ManagedOperation
public String convert(String input) {
return "cat";
}
}
}