INT-4133: Merge Java DSL Core functionality
JIRA: https://jira.spring.io/browse/INT-4133 Mostly copy/paste and changes according Java 8 and Reactor 3.0 foundations Fix `IntegrationFlow` JavaDocs Increase timeout in the `AbstractCorrelatingMessageHandlerTests` Polishing - reduce timeout after first expire attempt
This commit is contained in:
committed by
Gary Russell
parent
2f0b377cfb
commit
bde1efa9ee
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.beans.Introspector;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.integration.config.IntegrationConfigurationInitializer;
|
||||
import org.springframework.integration.dsl.IntegrationComponentSpec;
|
||||
import org.springframework.integration.dsl.context.IntegrationFlowContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The Java DSL Integration infrastructure {@code beanFactory} initializer.
|
||||
* Registers {@link IntegrationFlowBeanPostProcessor} and checks if all
|
||||
* {@link IntegrationComponentSpec} are extracted to the target object using
|
||||
* {@link IntegrationComponentSpec#get()}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*
|
||||
* @see org.springframework.integration.config.IntegrationConfigurationBeanFactoryPostProcessor
|
||||
*/
|
||||
public class DslIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
|
||||
|
||||
private static final String INTEGRATION_FLOW_BPP_BEAN_NAME =
|
||||
Introspector.decapitalize(IntegrationFlowBeanPostProcessor.class.getName());
|
||||
|
||||
private static final String INTEGRATION_FLOW_CONTEXT_BEAN_NAME =
|
||||
Introspector.decapitalize(IntegrationFlowContext.class.getName());
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(BeanDefinitionRegistry.class, configurableListableBeanFactory,
|
||||
"To use Spring Integration Java DSL the 'beanFactory' has to be an instance of " +
|
||||
"'BeanDefinitionRegistry'. Consider using 'GenericApplicationContext' implementation."
|
||||
);
|
||||
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) configurableListableBeanFactory;
|
||||
if (!registry.containsBeanDefinition(INTEGRATION_FLOW_BPP_BEAN_NAME)) {
|
||||
registry.registerBeanDefinition(INTEGRATION_FLOW_BPP_BEAN_NAME,
|
||||
new RootBeanDefinition(IntegrationFlowBeanPostProcessor.class));
|
||||
registry.registerBeanDefinition(INTEGRATION_FLOW_CONTEXT_BEAN_NAME,
|
||||
new RootBeanDefinition(IntegrationFlowContext.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.FixedSubscriberChannel;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.dsl.ComponentsRegistration;
|
||||
import org.springframework.integration.dsl.ConsumerEndpointSpec;
|
||||
import org.springframework.integration.dsl.IntegrationComponentSpec;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlowBuilder;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.SourcePollingChannelAdapterSpec;
|
||||
import org.springframework.integration.dsl.StandardIntegrationFlow;
|
||||
import org.springframework.integration.dsl.support.MessageChannelReference;
|
||||
import org.springframework.integration.support.context.NamedComponent;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} to parse {@link IntegrationFlow} beans and
|
||||
* register their components as beans in the provided {@link BeanFactory},
|
||||
* if necessary.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*/
|
||||
public class IntegrationFlowBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware,
|
||||
SmartInitializingSingleton {
|
||||
|
||||
private final Set<ApplicationListener<?>> applicationListeners = new HashSet<ApplicationListener<?>>();
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
|
||||
"To use Spring Integration Java DSL the 'beanFactory' has to be an instance of " +
|
||||
"'ConfigurableListableBeanFactory'. Consider using 'GenericApplicationContext' implementation."
|
||||
);
|
||||
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
|
||||
this.autowiredAnnotationBeanPostProcessor.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof StandardIntegrationFlow) {
|
||||
return processStandardIntegrationFlow((StandardIntegrationFlow) bean, beanName);
|
||||
}
|
||||
else if (bean instanceof IntegrationFlow) {
|
||||
return processIntegrationFlowImpl((IntegrationFlow) bean, beanName);
|
||||
}
|
||||
if (bean instanceof IntegrationComponentSpec) {
|
||||
processIntegrationComponentSpec((IntegrationComponentSpec<?, ?>) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
if (this.beanFactory.containsBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
|
||||
ApplicationEventMulticaster multicaster =
|
||||
(ApplicationEventMulticaster) this.beanFactory.getBean(
|
||||
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME);
|
||||
this.applicationListeners.forEach(multicaster::addApplicationListener);
|
||||
}
|
||||
|
||||
for (String beanName : this.beanFactory.getBeanNamesForType(IntegrationFlow.class)) {
|
||||
if (this.beanFactory.containsBeanDefinition(beanName)) {
|
||||
String scope = this.beanFactory.getBeanDefinition(beanName).getScope();
|
||||
if (StringUtils.hasText(scope) && !BeanDefinition.SCOPE_SINGLETON.equals(scope)) {
|
||||
throw new BeanCreationNotAllowedException(beanName, "IntegrationFlows can not be scoped beans. " +
|
||||
"Any dependant beans are registered as singletons, meanwhile IntegrationFlow is just a " +
|
||||
"logical container for them. \n" +
|
||||
"Consider to use [IntegrationFlowContext] for manual registration of IntegrationFlows.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object processStandardIntegrationFlow(StandardIntegrationFlow flow, String beanName) {
|
||||
String flowNamePrefix = beanName + ".";
|
||||
int subFlowNameIndex = 0;
|
||||
int channelNameIndex = 0;
|
||||
boolean registerSingleton = flow.isRegisterComponents();
|
||||
|
||||
|
||||
List<Object> integrationComponents = new ArrayList<>(flow.getIntegrationComponents());
|
||||
for (int i = 0; i < integrationComponents.size(); i++) {
|
||||
Object component = integrationComponents.get(i);
|
||||
if (component instanceof ConsumerEndpointSpec) {
|
||||
ConsumerEndpointSpec<?, ?> endpointSpec = (ConsumerEndpointSpec<?, ?>) component;
|
||||
MessageHandler messageHandler = endpointSpec.get().getT2();
|
||||
ConsumerEndpointFactoryBean endpoint = endpointSpec.get().getT1();
|
||||
String id = endpointSpec.getId();
|
||||
|
||||
Collection<?> messageHandlers = this.beanFactory.getBeansOfType(messageHandler.getClass(), false,
|
||||
false).values();
|
||||
|
||||
if (!messageHandlers.contains(messageHandler)) {
|
||||
String handlerBeanName = generateBeanName(messageHandler);
|
||||
String[] handlerAlias = id != null
|
||||
? new String[] { id + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX }
|
||||
: null;
|
||||
|
||||
registerComponent(messageHandler, handlerBeanName, beanName, registerSingleton);
|
||||
if (handlerAlias != null) {
|
||||
for (String alias : handlerAlias) {
|
||||
this.beanFactory.registerAlias(handlerBeanName, alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String endpointBeanName = id;
|
||||
if (endpointBeanName == null) {
|
||||
endpointBeanName = generateBeanName(endpoint);
|
||||
}
|
||||
registerComponent(endpoint, endpointBeanName, beanName, registerSingleton);
|
||||
integrationComponents.set(i, endpoint);
|
||||
}
|
||||
else {
|
||||
Collection<?> values = this.beanFactory.getBeansOfType(component.getClass(), false, false).values();
|
||||
if (!values.contains(component)) {
|
||||
if (component instanceof AbstractMessageChannel) {
|
||||
String channelBeanName = ((AbstractMessageChannel) component).getComponentName();
|
||||
if (channelBeanName == null) {
|
||||
channelBeanName = flowNamePrefix + "channel" +
|
||||
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + channelNameIndex++;
|
||||
}
|
||||
registerComponent(component, channelBeanName, beanName, registerSingleton);
|
||||
}
|
||||
else if (component instanceof MessageChannelReference) {
|
||||
String channelBeanName = ((MessageChannelReference) component).getName();
|
||||
if (!this.beanFactory.containsBean(channelBeanName)) {
|
||||
DirectChannel directChannel = new DirectChannel();
|
||||
registerComponent(directChannel, channelBeanName, beanName, registerSingleton);
|
||||
integrationComponents.set(i, directChannel);
|
||||
}
|
||||
}
|
||||
else if (component instanceof FixedSubscriberChannel) {
|
||||
FixedSubscriberChannel fixedSubscriberChannel = (FixedSubscriberChannel) component;
|
||||
String channelBeanName = fixedSubscriberChannel.getComponentName();
|
||||
if ("Unnamed fixed subscriber channel".equals(channelBeanName)) {
|
||||
channelBeanName = flowNamePrefix + "channel" +
|
||||
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + channelNameIndex++;
|
||||
}
|
||||
registerComponent(component, channelBeanName, beanName, registerSingleton);
|
||||
}
|
||||
else if (component instanceof SourcePollingChannelAdapterSpec) {
|
||||
SourcePollingChannelAdapterSpec spec = (SourcePollingChannelAdapterSpec) component;
|
||||
Collection<Object> componentsToRegister = spec.getComponentsToRegister();
|
||||
if (!CollectionUtils.isEmpty(componentsToRegister)) {
|
||||
componentsToRegister.stream()
|
||||
.filter(o -> !this.beanFactory.getBeansOfType(o.getClass(), false, false)
|
||||
.values()
|
||||
.contains(o))
|
||||
.forEach(o -> registerComponent(o, generateBeanName(o))
|
||||
);
|
||||
}
|
||||
SourcePollingChannelAdapterFactoryBean pollingChannelAdapterFactoryBean = spec.get().getT1();
|
||||
String id = spec.getId();
|
||||
if (!StringUtils.hasText(id)) {
|
||||
id = generateBeanName(pollingChannelAdapterFactoryBean);
|
||||
}
|
||||
registerComponent(pollingChannelAdapterFactoryBean, id, beanName, registerSingleton);
|
||||
integrationComponents.set(i, pollingChannelAdapterFactoryBean);
|
||||
|
||||
MessageSource<?> messageSource = spec.get().getT2();
|
||||
if (!this.beanFactory.getBeansOfType(messageSource.getClass(), false, false)
|
||||
.values()
|
||||
.contains(messageSource)) {
|
||||
String messageSourceId = id + ".source";
|
||||
if (messageSource instanceof NamedComponent
|
||||
&& ((NamedComponent) messageSource).getComponentName() != null) {
|
||||
messageSourceId = ((NamedComponent) messageSource).getComponentName();
|
||||
}
|
||||
registerComponent(messageSource, messageSourceId, beanName, registerSingleton);
|
||||
}
|
||||
}
|
||||
else if (component instanceof StandardIntegrationFlow) {
|
||||
String subFlowBeanName = flowNamePrefix + "subFlow" +
|
||||
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + subFlowNameIndex++;
|
||||
registerComponent(component, subFlowBeanName, beanName, registerSingleton);
|
||||
}
|
||||
else {
|
||||
String generateBeanName = generateBeanName(component);
|
||||
registerComponent(component, generateBeanName, beanName, registerSingleton);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flow.setIntegrationComponents(integrationComponents);
|
||||
return flow;
|
||||
}
|
||||
|
||||
private Object processIntegrationFlowImpl(IntegrationFlow flow, String beanName) {
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(beanName + ".input");
|
||||
flow.configure(flowBuilder);
|
||||
Object standardIntegrationFlow = processStandardIntegrationFlow(flowBuilder.get(), beanName);
|
||||
return isLambda(flow) ? standardIntegrationFlow : flow;
|
||||
}
|
||||
|
||||
private void processIntegrationComponentSpec(IntegrationComponentSpec<?, ?> bean) {
|
||||
registerComponent(bean.get(), generateBeanName(bean.get()), null, false);
|
||||
if (bean instanceof ComponentsRegistration) {
|
||||
Collection<Object> componentsToRegister = ((ComponentsRegistration) bean).getComponentsToRegister();
|
||||
if (!CollectionUtils.isEmpty(componentsToRegister)) {
|
||||
componentsToRegister.stream()
|
||||
.filter(component -> !this.beanFactory.getBeansOfType(component.getClass(), false, false)
|
||||
.values()
|
||||
.contains(component))
|
||||
.forEach(component -> registerComponent(component, generateBeanName(component)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerComponent(Object component, String beanName) {
|
||||
registerComponent(component, beanName, null, true);
|
||||
}
|
||||
|
||||
private void registerComponent(Object component, String beanName, String parentName, boolean registerSingleton) {
|
||||
if (component instanceof ApplicationListener) {
|
||||
this.applicationListeners.add((ApplicationListener<?>) component);
|
||||
}
|
||||
this.autowiredAnnotationBeanPostProcessor.processInjection(component);
|
||||
this.beanFactory.initializeBean(component, beanName);
|
||||
if (registerSingleton) {
|
||||
this.beanFactory.registerSingleton(beanName, component);
|
||||
if (parentName != null) {
|
||||
this.beanFactory.registerDependentBean(parentName, beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String generateBeanName(Object instance) {
|
||||
if (instance instanceof NamedComponent && ((NamedComponent) instance).getComponentName() != null) {
|
||||
return ((NamedComponent) instance).getComponentName();
|
||||
}
|
||||
String generatedBeanName = instance.getClass().getName();
|
||||
String id = generatedBeanName;
|
||||
int counter = -1;
|
||||
while (counter == -1 || this.beanFactory.containsBean(id)) {
|
||||
counter++;
|
||||
id = generatedBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static boolean isLambda(Object o) {
|
||||
Class<?> aClass = o.getClass();
|
||||
return aClass.isSynthetic() && !aClass.isAnonymousClass() && !aClass.isLocalClass();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides config classes of the Spring Integration Java DSL.
|
||||
*/
|
||||
package org.springframework.integration.config.dsl;
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.router.AbstractMessageRouter;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link MessageHandlerSpec} for {@link AbstractMessageRouter}s.
|
||||
*
|
||||
* @param <S> the target {@link AbstractRouterSpec} implementation type.
|
||||
* @param <R> the {@link AbstractMessageRouter} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AbstractRouterSpec<S extends AbstractRouterSpec<S, R>, R extends AbstractMessageRouter>
|
||||
extends MessageHandlerSpec<S, R> implements ComponentsRegistration {
|
||||
|
||||
protected final List<Object> subFlows = new ArrayList<Object>();
|
||||
|
||||
private boolean defaultToParentFlow;
|
||||
|
||||
AbstractRouterSpec(R router) {
|
||||
this.target = router;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ignoreSendFailures the ignoreSendFailures.
|
||||
* @return the router spec.
|
||||
* @see AbstractMessageRouter#setIgnoreSendFailures(boolean)
|
||||
*/
|
||||
public S ignoreSendFailures(boolean ignoreSendFailures) {
|
||||
this.target.setIgnoreSendFailures(ignoreSendFailures);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param applySequence the applySequence.
|
||||
* @return the router spec.
|
||||
* @see AbstractMessageRouter#setApplySequence(boolean)
|
||||
*/
|
||||
public S applySequence(boolean applySequence) {
|
||||
this.target.setApplySequence(applySequence);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link MessageChannel} bean name as a default output from the router.
|
||||
* @param channelName the {@link MessageChannel} bean name.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
* @see AbstractMessageRouter#setDefaultOutputChannelName(String)
|
||||
*/
|
||||
public S defaultOutputChannel(String channelName) {
|
||||
this.target.setDefaultOutputChannelName(channelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link MessageChannel} as a default output from the router.
|
||||
* @param channel the {@link MessageChannel} to use.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
* @see AbstractMessageRouter#setDefaultOutputChannel(MessageChannel)
|
||||
*/
|
||||
public S defaultOutputChannel(MessageChannel channel) {
|
||||
this.target.setDefaultOutputChannel(channel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an {@link IntegrationFlow} as an output from the router when no any other mapping has matched.
|
||||
* @param subFlow the {@link IntegrationFlow} for default mapping.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
*/
|
||||
public S defaultSubFlowMapping(IntegrationFlow subFlow) {
|
||||
Assert.notNull(subFlow);
|
||||
DirectChannel channel = new DirectChannel();
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(channel);
|
||||
subFlow.configure(flowBuilder);
|
||||
|
||||
this.subFlows.add(flowBuilder);
|
||||
|
||||
return defaultOutputChannel(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a default output mapping of the router to the parent flow.
|
||||
* Use the next, after router, parent flow {@link MessageChannel} as a
|
||||
* {@link AbstractMessageRouter#setDefaultOutputChannel(MessageChannel)} of this router.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
*/
|
||||
public S defaultOutputToParentFlow() {
|
||||
this.defaultToParentFlow = true;
|
||||
return _this();
|
||||
}
|
||||
|
||||
boolean isDefaultToParentFlow() {
|
||||
return this.defaultToParentFlow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return this.subFlows;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.integration.aggregator.AggregatingMessageHandler;
|
||||
import org.springframework.integration.aggregator.DefaultAggregatingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.ExpressionEvaluatingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.MessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.MethodInvokingMessageGroupProcessor;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link CorrelationHandlerSpec} for an {@link AggregatingMessageHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class AggregatorSpec extends CorrelationHandlerSpec<AggregatorSpec, AggregatingMessageHandler> {
|
||||
|
||||
AggregatorSpec() {
|
||||
super(new InternalAggregatingMessageHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with {@link org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy}
|
||||
* and {@link org.springframework.integration.aggregator.MethodInvokingReleaseStrategy} using the target
|
||||
* object which should have methods annotated appropriately for each function.
|
||||
* Also set the output processor.
|
||||
* @param target the target object.
|
||||
* @return the handler spec.
|
||||
*/
|
||||
public AggregatorSpec processor(Object target) {
|
||||
return processor(target, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with {@link org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy}
|
||||
* and {@link org.springframework.integration.aggregator.MethodInvokingReleaseStrategy} using the target
|
||||
* object which should have methods annotated appropriately for each function.
|
||||
* Also set the output processor.
|
||||
* @param target the target object.
|
||||
* @param methodName The method name for the output processor (or 'null' in which case, the
|
||||
* target object must have an {@link org.springframework.integration.annotation.Aggregator}
|
||||
* annotation).
|
||||
* @return the handler spec.
|
||||
*/
|
||||
public AggregatorSpec processor(Object target, String methodName) {
|
||||
super.processor(target);
|
||||
return this.outputProcessor(methodName != null
|
||||
? new MethodInvokingMessageGroupProcessor(target, methodName)
|
||||
: new MethodInvokingMessageGroupProcessor(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression to determine the output message from the released group. Defaults to a message
|
||||
* with a payload that is a collection of payloads from the input messages.
|
||||
* @param expression the expression.
|
||||
* @return the aggregator spec.
|
||||
*/
|
||||
public AggregatorSpec outputExpression(String expression) {
|
||||
return this.outputProcessor(new ExpressionEvaluatingMessageGroupProcessor(expression));
|
||||
}
|
||||
|
||||
/**
|
||||
* A processor to determine the output message from the released group. Defaults to a message
|
||||
* with a payload that is a collection of payloads from the input messages.
|
||||
* @param outputProcessor the processor.
|
||||
* @return the aggregator spec.
|
||||
*/
|
||||
public AggregatorSpec outputProcessor(MessageGroupProcessor outputProcessor) {
|
||||
Assert.notNull(outputProcessor, "'outputProcessor' must not be null.");
|
||||
((InternalAggregatingMessageHandler) this.handler).getOutputProcessor().setDelegate(outputProcessor);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expireGroupsUponCompletion the expireGroupsUponCompletion.
|
||||
* @return the aggregator spec.
|
||||
* @see AggregatingMessageHandler#setExpireGroupsUponCompletion(boolean)
|
||||
*/
|
||||
public AggregatorSpec expireGroupsUponCompletion(boolean expireGroupsUponCompletion) {
|
||||
this.handler.setExpireGroupsUponCompletion(expireGroupsUponCompletion);
|
||||
return _this();
|
||||
}
|
||||
|
||||
//TODO Move the logic to the AggregatingMessageHandler
|
||||
private static class InternalAggregatingMessageHandler extends AggregatingMessageHandler {
|
||||
|
||||
InternalAggregatingMessageHandler() {
|
||||
super(new MessageGroupProcessorWrapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageGroupProcessorWrapper getOutputProcessor() {
|
||||
return (MessageGroupProcessorWrapper) super.getOutputProcessor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MessageGroupProcessorWrapper implements MessageGroupProcessor, BeanFactoryAware {
|
||||
|
||||
MessageGroupProcessorWrapper() {
|
||||
super();
|
||||
}
|
||||
|
||||
private MessageGroupProcessor delegate = new DefaultAggregatingMessageGroupProcessor();
|
||||
|
||||
void setDelegate(MessageGroupProcessor delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object processMessageGroup(MessageGroup group) {
|
||||
return this.delegate.processMessageGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (this.delegate instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.delegate).setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.aggregator.BarrierMessageHandler;
|
||||
import org.springframework.integration.aggregator.CorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.DefaultAggregatingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.MessageGroupProcessor;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* A {@link MessageHandlerSpec} for the {@link BarrierMessageHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BarrierSpec extends ConsumerEndpointSpec<BarrierSpec, BarrierMessageHandler> {
|
||||
|
||||
private final long timeout;
|
||||
|
||||
private MessageGroupProcessor outputProcessor = new DefaultAggregatingMessageGroupProcessor();
|
||||
|
||||
private CorrelationStrategy correlationStrategy =
|
||||
new HeaderAttributeCorrelationStrategy(IntegrationMessageHeaderAccessor.CORRELATION_ID);
|
||||
|
||||
private boolean requiresReply;
|
||||
|
||||
private long sendTimeout = -1;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private boolean async;
|
||||
|
||||
BarrierSpec(long timeout) {
|
||||
super(null);
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public BarrierSpec outputProcessor(MessageGroupProcessor outputProcessor) {
|
||||
Assert.notNull(outputProcessor, "'outputProcessor' must not be null.");
|
||||
this.outputProcessor = outputProcessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BarrierSpec correlationStrategy(CorrelationStrategy correlationStrategy) {
|
||||
Assert.notNull(correlationStrategy, "'correlationStrategy' must not be null.");
|
||||
this.correlationStrategy = correlationStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarrierSpec requiresReply(boolean requiresReply) {
|
||||
this.requiresReply = requiresReply;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarrierSpec sendTimeout(long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarrierSpec order(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarrierSpec async(boolean async) {
|
||||
this.async = async;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tuple2<ConsumerEndpointFactoryBean, BarrierMessageHandler> doGet() {
|
||||
BarrierMessageHandler barrierMessageHandler =
|
||||
new BarrierMessageHandler(this.timeout, this.outputProcessor, this.correlationStrategy);
|
||||
barrierMessageHandler.setAdviceChain(this.adviceChain);
|
||||
barrierMessageHandler.setRequiresReply(this.requiresReply);
|
||||
barrierMessageHandler.setSendTimeout(this.sendTimeout);
|
||||
barrierMessageHandler.setAsync(this.async);
|
||||
barrierMessageHandler.setOrder(this.order);
|
||||
this.endpointFactoryBean.setHandler(barrierMessageHandler);
|
||||
return Tuples.of(this.endpointFactoryBean, barrierMessageHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* The marker interface for the {@link IntegrationComponentSpec} implementation,
|
||||
* when there is need to register as beans not only the target spec's components,
|
||||
* but some additional components, e.g. {@code subflows} from
|
||||
* {@link org.springframework.integration.dsl.RouterSpec}.
|
||||
* <p>
|
||||
* For internal use only.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ComponentsRegistration {
|
||||
|
||||
Collection<Object> getComponentsToRegister();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.integration.handler.AbstractMessageHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.transaction.TransactionInterceptorBuilder;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
|
||||
/**
|
||||
* A {@link EndpointSpec} for consumer endpoints.
|
||||
*
|
||||
* @param <S> the target {@link ConsumerEndpointSpec} implementation type.
|
||||
* @param <H> the target {@link MessageHandler} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>, H extends MessageHandler>
|
||||
extends EndpointSpec<S, ConsumerEndpointFactoryBean, H> {
|
||||
|
||||
protected final List<Advice> adviceChain = new LinkedList<>();
|
||||
|
||||
protected ConsumerEndpointSpec(H messageHandler) {
|
||||
super(messageHandler);
|
||||
if (messageHandler != null) {
|
||||
this.endpointFactoryBean.setHandler(messageHandler);
|
||||
}
|
||||
this.endpointFactoryBean.setAdviceChain(this.adviceChain);
|
||||
if (messageHandler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) messageHandler).setAdviceChain(this.adviceChain);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public S phase(int phase) {
|
||||
this.endpointFactoryBean.setPhase(phase);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S autoStartup(boolean autoStartup) {
|
||||
this.endpointFactoryBean.setAutoStartup(autoStartup);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S poller(PollerMetadata pollerMetadata) {
|
||||
this.endpointFactoryBean.setPollerMetadata(pollerMetadata);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a list of {@link Advice} objects to be applied, in nested order, to the endpoint's handler.
|
||||
* The advice objects are applied only to the handler.
|
||||
* @param advice the advice chain.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public S advice(Advice... advice) {
|
||||
this.adviceChain.addAll(Arrays.asList(advice));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with the
|
||||
* provided {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
|
||||
* for the {@code pollingTask}.
|
||||
* @param transactionManager the {@link PlatformTransactionManager} to use.
|
||||
* @return the spec.
|
||||
*/
|
||||
public S transactional(PlatformTransactionManager transactionManager) {
|
||||
return transactional(transactionManager, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with the
|
||||
* provided {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
|
||||
* for the {@code pollingTask}.
|
||||
* @param transactionManager the {@link PlatformTransactionManager} to use.
|
||||
* @param handleMessageAdvice the flag to indicate the target {@link Advice} type:
|
||||
* {@code false} - regular {@link TransactionInterceptor};
|
||||
* {@code true} - {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice} extension.
|
||||
* @return the spec.
|
||||
*/
|
||||
public S transactional(PlatformTransactionManager transactionManager, boolean handleMessageAdvice) {
|
||||
return transactional(new TransactionInterceptorBuilder(handleMessageAdvice)
|
||||
.transactionManager(transactionManager)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} for the {@code pollingTask}.
|
||||
* @param transactionInterceptor the {@link TransactionInterceptor} to use.
|
||||
* @return the spec.
|
||||
* @see TransactionInterceptorBuilder
|
||||
*/
|
||||
public S transactional(TransactionInterceptor transactionInterceptor) {
|
||||
return advice(transactionInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with default {@code PlatformTransactionManager}
|
||||
* and {@link DefaultTransactionAttribute} for the {@code pollingTask}.
|
||||
* @return the spec.
|
||||
*/
|
||||
public S transactional() {
|
||||
return transactional(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with default {@code PlatformTransactionManager}
|
||||
* and {@link DefaultTransactionAttribute} for the {@code pollingTask}.
|
||||
* @param handleMessageAdvice the flag to indicate the target {@link Advice} type:
|
||||
* {@code false} - regular {@link TransactionInterceptor};
|
||||
* {@code true} - {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice} extension.
|
||||
* @return the spec.
|
||||
*/
|
||||
public S transactional(boolean handleMessageAdvice) {
|
||||
TransactionInterceptor transactionInterceptor = new TransactionInterceptorBuilder(handleMessageAdvice).build();
|
||||
this.componentToRegister.add(transactionInterceptor);
|
||||
return transactional(transactionInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requiresReply the requiresReply.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractReplyProducingMessageHandler#setRequiresReply(boolean)
|
||||
*/
|
||||
public S requiresReply(boolean requiresReply) {
|
||||
assertHandler();
|
||||
if (this.handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) this.handler).setRequiresReply(requiresReply);
|
||||
}
|
||||
else {
|
||||
logger.warn("'requiresReply' can be applied only for AbstractReplyProducingMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sendTimeout the send timeout.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractReplyProducingMessageHandler#setSendTimeout(long)
|
||||
*/
|
||||
public S sendTimeout(long sendTimeout) {
|
||||
assertHandler();
|
||||
if (this.handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) this.handler).setSendTimeout(sendTimeout);
|
||||
}
|
||||
else {
|
||||
logger.warn("'sendTimeout' can be applied only for AbstractReplyProducingMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param order the order.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractMessageHandler#setOrder(int)
|
||||
*/
|
||||
public S order(int order) {
|
||||
assertHandler();
|
||||
if (this.handler instanceof AbstractMessageHandler) {
|
||||
((AbstractMessageHandler) this.handler).setOrder(order);
|
||||
}
|
||||
else {
|
||||
logger.warn("'order' can be applied only for AbstractMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow async replies. If the handler reply is a {@code ListenableFuture} send
|
||||
* the output when it is satisfied rather than sending the future as the result.
|
||||
* Only subclasses that support this feature should set it.
|
||||
* @param async true to allow.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractReplyProducingMessageHandler#setAsync(boolean)
|
||||
*/
|
||||
public S async(boolean async) {
|
||||
assertHandler();
|
||||
if (this.handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) this.handler).setAsync(async);
|
||||
}
|
||||
else {
|
||||
logger.warn("'async' can be applied only for AbstractReplyProducingMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler;
|
||||
import org.springframework.integration.aggregator.CorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.ExpressionEvaluatingCorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.ExpressionEvaluatingReleaseStrategy;
|
||||
import org.springframework.integration.aggregator.ReleaseStrategy;
|
||||
import org.springframework.integration.config.CorrelationStrategyFactoryBean;
|
||||
import org.springframework.integration.config.ReleaseStrategyFactoryBean;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link MessageHandlerSpec} for an {@link AbstractCorrelatingMessageHandler}.
|
||||
*
|
||||
* @param <S> the target {@link CorrelationHandlerSpec} implementation type.
|
||||
* @param <H> the {@link AbstractCorrelatingMessageHandler} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class
|
||||
CorrelationHandlerSpec<S extends CorrelationHandlerSpec<S, H>, H extends AbstractCorrelatingMessageHandler>
|
||||
extends ConsumerEndpointSpec<S, H> {
|
||||
|
||||
private final List<Advice> forceReleaseAdviceChain = new LinkedList<Advice>();
|
||||
|
||||
protected CorrelationHandlerSpec(H messageHandler) {
|
||||
super(messageHandler);
|
||||
messageHandler.setForceReleaseAdviceChain(this.forceReleaseAdviceChain);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messageStore the message group store.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setMessageStore(MessageGroupStore)
|
||||
*/
|
||||
public S messageStore(MessageGroupStore messageStore) {
|
||||
Assert.notNull(messageStore, "'messageStore' must not be null.");
|
||||
this.handler.setMessageStore(messageStore);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sendPartialResultOnExpiry the sendPartialResultOnExpiry.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setSendPartialResultOnExpiry(boolean)
|
||||
*/
|
||||
public S sendPartialResultOnExpiry(boolean sendPartialResultOnExpiry) {
|
||||
this.handler.setSendPartialResultOnExpiry(sendPartialResultOnExpiry);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param minimumTimeoutForEmptyGroups the minimumTimeoutForEmptyGroups
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setMinimumTimeoutForEmptyGroups(long)
|
||||
*/
|
||||
public S minimumTimeoutForEmptyGroups(long minimumTimeoutForEmptyGroups) {
|
||||
this.handler.setMinimumTimeoutForEmptyGroups(minimumTimeoutForEmptyGroups);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with a group timeout expression that evaluates to
|
||||
* this constant value.
|
||||
* @param groupTimeout the group timeout in milliseconds.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setGroupTimeoutExpression
|
||||
* @see ValueExpression
|
||||
*/
|
||||
public S groupTimeout(long groupTimeout) {
|
||||
this.handler.setGroupTimeoutExpression(new ValueExpression<>(groupTimeout));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param groupTimeoutExpression the group timeout expression string.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setGroupTimeoutExpression
|
||||
*/
|
||||
public S groupTimeoutExpression(String groupTimeoutExpression) {
|
||||
Assert.hasText(groupTimeoutExpression, "'groupTimeoutExpression' must not be empty string.");
|
||||
this.handler.setGroupTimeoutExpression(PARSER.parseExpression(groupTimeoutExpression));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with a function that will be invoked to resolve the group timeout,
|
||||
* based on the message group.
|
||||
* Usually used with a JDK8 lambda:
|
||||
* <p>{@code .groupTimeout(g -> g.size() * 2000L)}.
|
||||
* @param groupTimeoutFunction a function invoked to resolve the group timeout in milliseconds.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setGroupTimeoutExpression
|
||||
*/
|
||||
public S groupTimeout(Function<MessageGroup, Long> groupTimeoutFunction) {
|
||||
this.handler.setGroupTimeoutExpression(new FunctionExpression<>(groupTimeoutFunction));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param taskScheduler the task scheduler.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setTaskScheduler(TaskScheduler)
|
||||
*/
|
||||
public S taskScheduler(TaskScheduler taskScheduler) {
|
||||
Assert.notNull(taskScheduler, "'taskScheduler' must not be null.");
|
||||
this.handler.setTaskScheduler(taskScheduler);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param discardChannel the discard channel.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setDiscardChannel(MessageChannel)
|
||||
*/
|
||||
public S discardChannel(MessageChannel discardChannel) {
|
||||
Assert.notNull(discardChannel, "'discardChannel' must not be null.");
|
||||
this.handler.setDiscardChannel(discardChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param discardChannelName the discard channel.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setDiscardChannelName(String)
|
||||
*/
|
||||
public S discardChannel(String discardChannelName) {
|
||||
Assert.hasText(discardChannelName, "'discardChannelName' must not be empty.");
|
||||
this.handler.setDiscardChannelName(discardChannelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with {@link org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy}
|
||||
* and {@link org.springframework.integration.aggregator.MethodInvokingReleaseStrategy} using the target
|
||||
* object which should have methods annotated appropriately for each function.
|
||||
* @param target the target object,
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setCorrelationStrategy(CorrelationStrategy)
|
||||
* @see AbstractCorrelatingMessageHandler#setReleaseStrategy(ReleaseStrategy)
|
||||
*/
|
||||
public S processor(Object target) {
|
||||
try {
|
||||
CorrelationStrategyFactoryBean correlationStrategyFactoryBean = new CorrelationStrategyFactoryBean();
|
||||
correlationStrategyFactoryBean.setTarget(target);
|
||||
correlationStrategyFactoryBean.afterPropertiesSet();
|
||||
ReleaseStrategyFactoryBean releaseStrategyFactoryBean = new ReleaseStrategyFactoryBean();
|
||||
releaseStrategyFactoryBean.setTarget(target);
|
||||
releaseStrategyFactoryBean.afterPropertiesSet();
|
||||
return correlationStrategy(correlationStrategyFactoryBean.getObject())
|
||||
.releaseStrategy(releaseStrategyFactoryBean.getObject());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with an {@link ExpressionEvaluatingCorrelationStrategy} for the
|
||||
* given expression.
|
||||
* @param correlationExpression the correlation expression.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setCorrelationStrategy(CorrelationStrategy)
|
||||
*/
|
||||
public S correlationExpression(String correlationExpression) {
|
||||
return correlationStrategy(new ExpressionEvaluatingCorrelationStrategy(correlationExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with an
|
||||
* {@link org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy}
|
||||
* for the target object and method name.
|
||||
* @param target the target object.
|
||||
* @param methodName the method name.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setCorrelationStrategy(CorrelationStrategy)
|
||||
*/
|
||||
public S correlationStrategy(Object target, String methodName) {
|
||||
try {
|
||||
CorrelationStrategyFactoryBean correlationStrategyFactoryBean = new CorrelationStrategyFactoryBean();
|
||||
correlationStrategyFactoryBean.setTarget(target);
|
||||
correlationStrategyFactoryBean.setMethodName(methodName);
|
||||
correlationStrategyFactoryBean.afterPropertiesSet();
|
||||
return correlationStrategy(correlationStrategyFactoryBean.getObject());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param correlationStrategy the correlation strategy.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setCorrelationStrategy(CorrelationStrategy)
|
||||
*/
|
||||
public S correlationStrategy(CorrelationStrategy correlationStrategy) {
|
||||
this.handler.setCorrelationStrategy(correlationStrategy);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with an {@link ExpressionEvaluatingReleaseStrategy} for the
|
||||
* given expression.
|
||||
* @param releaseExpression the correlation expression.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setReleaseStrategy(ReleaseStrategy)
|
||||
*/
|
||||
public S releaseExpression(String releaseExpression) {
|
||||
return releaseStrategy(new ExpressionEvaluatingReleaseStrategy(releaseExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the handler with an
|
||||
* {@link org.springframework.integration.aggregator.MethodInvokingReleaseStrategy}
|
||||
* for the target object and method name.
|
||||
* @param target the target object.
|
||||
* @param methodName the method name.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setReleaseStrategy(ReleaseStrategy)
|
||||
*/
|
||||
public S releaseStrategy(Object target, String methodName) {
|
||||
try {
|
||||
ReleaseStrategyFactoryBean releaseStrategyFactoryBean = new ReleaseStrategyFactoryBean();
|
||||
releaseStrategyFactoryBean.setTarget(target);
|
||||
releaseStrategyFactoryBean.setMethodName(methodName);
|
||||
releaseStrategyFactoryBean.afterPropertiesSet();
|
||||
return releaseStrategy(releaseStrategyFactoryBean.getObject());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param releaseStrategy the release strategy.
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setReleaseStrategy(ReleaseStrategy)
|
||||
*/
|
||||
public S releaseStrategy(ReleaseStrategy releaseStrategy) {
|
||||
this.handler.setReleaseStrategy(releaseStrategy);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire (completely remove) a group if it is completed due to timeout.
|
||||
* Default {@code true} for aggregator and {@code false} for resequencer.
|
||||
* @param expireGroupsUponTimeout the expireGroupsUponTimeout to set
|
||||
* @return the handler spec.
|
||||
* @see AbstractCorrelatingMessageHandler#setExpireGroupsUponTimeout
|
||||
*/
|
||||
public S expireGroupsUponTimeout(boolean expireGroupsUponTimeout) {
|
||||
this.handler.setExpireGroupsUponTimeout(expireGroupsUponTimeout);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a list of {@link Advice} objects to be applied to the
|
||||
* {@code forceComplete()} operation.
|
||||
* @param advice the advice chain.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public S forceReleaseAdvice(Advice... advice) {
|
||||
this.forceReleaseAdviceChain.addAll(Arrays.asList(advice));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to obtain a {@code Lock} based on the {@code groupId} for concurrent operations
|
||||
* on the {@code MessageGroup}.
|
||||
* By default, an internal {@code DefaultLockRegistry} is used.
|
||||
* Use of a distributed {@link LockRegistry}, such as the {@code RedisLockRegistry},
|
||||
* ensures only one instance of the aggregator will operate on a group concurrently.
|
||||
* @param lockRegistry the {@link LockRegistry} to use.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public S lockRegistry(LockRegistry lockRegistry) {
|
||||
Assert.notNull(lockRegistry, "'lockRegistry' must not be null.");
|
||||
this.handler.setLockRegistry(lockRegistry);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.handler.DelayHandler;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ConsumerEndpointSpec} for a {@link DelayHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class DelayerEndpointSpec extends ConsumerEndpointSpec<DelayerEndpointSpec, DelayHandler> {
|
||||
|
||||
private final List<Advice> delayedAdvice = new LinkedList<Advice>();
|
||||
|
||||
DelayerEndpointSpec(DelayHandler delayHandler) {
|
||||
super(delayHandler);
|
||||
Assert.notNull(delayHandler, "'delayHandler' must not be null.");
|
||||
this.handler.setDelayedAdviceChain(this.delayedAdvice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultDelay the defaultDelay.
|
||||
* @return the endpoint spec.
|
||||
* @see DelayHandler#setDefaultDelay(long)
|
||||
*/
|
||||
public DelayerEndpointSpec defaultDelay(long defaultDelay) {
|
||||
this.handler.setDefaultDelay(defaultDelay);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ignoreExpressionFailures the ignoreExpressionFailures.
|
||||
* @return the endpoint spec.
|
||||
* @see DelayHandler#setIgnoreExpressionFailures(boolean)
|
||||
*/
|
||||
public DelayerEndpointSpec ignoreExpressionFailures(boolean ignoreExpressionFailures) {
|
||||
this.handler.setIgnoreExpressionFailures(ignoreExpressionFailures);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messageStore the message store.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public DelayerEndpointSpec messageStore(MessageGroupStore messageStore) {
|
||||
this.handler.setMessageStore(messageStore);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a list of {@link Advice} objects that will be applied, in nested order,
|
||||
* when delayed messages are sent.
|
||||
* @param advice the advice chain.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public DelayerEndpointSpec delayedAdvice(Advice... advice) {
|
||||
this.delayedAdvice.addAll(Arrays.asList(advice));
|
||||
return _this();
|
||||
}
|
||||
|
||||
public DelayerEndpointSpec delayExpression(Expression delayExpression) {
|
||||
this.handler.setDelayExpression(delayExpression);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DelayerEndpointSpec delayExpression(String delayExpression) {
|
||||
this.handler.setDelayExpression(PARSER.parseExpression(delayExpression));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the function to determine delay value against {@link Message}.
|
||||
* Typically used with a Java 8 Lambda expression:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* .<Foo>delay("delayer", m -> m.getPayload().getDate(),
|
||||
* c -> c.advice(this.delayedAdvice).messageStore(this.messageStore()))
|
||||
* }
|
||||
* </pre>
|
||||
* @param delayFunction the {@link Function} to determine delay.
|
||||
* @param <P> the payload type.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public <P> DelayerEndpointSpec delayFunction(Function<Message<P>, Object> delayFunction) {
|
||||
this.handler.setDelayExpression(new FunctionExpression<>(delayFunction));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.core.GenericSelector;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.dsl.support.MessageChannelReference;
|
||||
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
|
||||
import org.springframework.integration.filter.MethodInvokingSelector;
|
||||
import org.springframework.integration.router.RecipientListRouter;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.core.DestinationResolutionException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* TODO Move the logic to the RecipientListRouter
|
||||
*/
|
||||
class DslRecipientListRouter extends RecipientListRouter {
|
||||
|
||||
private final List<Tuple2<?, ?>> recipients = new ArrayList<>();
|
||||
|
||||
void add(String channelName, Expression expression) {
|
||||
this.recipients.add(Tuples.of(channelName, expression));
|
||||
}
|
||||
|
||||
void add(String channelName, GenericSelector<?> selector) {
|
||||
this.recipients.add(Tuples.of(channelName, selector));
|
||||
}
|
||||
|
||||
void add(MessageChannel channel, Expression expression) {
|
||||
this.recipients.add(Tuples.of(channel, expression));
|
||||
}
|
||||
|
||||
void add(MessageChannel channel, GenericSelector<?> selector) {
|
||||
this.recipients.add(Tuples.of(channel, selector));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() throws Exception {
|
||||
List<Recipient> recipients = new ArrayList<Recipient>(this.recipients.size());
|
||||
|
||||
for (Tuple2<?, ?> recipient : this.recipients) {
|
||||
if (recipient.getT1() instanceof String) {
|
||||
recipients.add(new DslRecipient(new MessageChannelReference((String) recipient.getT1()),
|
||||
populateRecipientSelector(recipient.getT2())));
|
||||
}
|
||||
else {
|
||||
recipients.add(new Recipient((MessageChannel) recipient.getT1(),
|
||||
populateRecipientSelector(recipient.getT2())));
|
||||
}
|
||||
}
|
||||
|
||||
setRecipients(recipients);
|
||||
this.recipients.clear();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
private MessageSelector populateRecipientSelector(final Object recipientSelector) {
|
||||
if (recipientSelector instanceof String) {
|
||||
String expression = (String) recipientSelector;
|
||||
if (StringUtils.hasText(expression)) {
|
||||
ExpressionEvaluatingSelector selector = new ExpressionEvaluatingSelector(expression);
|
||||
selector.setBeanFactory(getBeanFactory());
|
||||
return selector;
|
||||
}
|
||||
}
|
||||
else if (recipientSelector instanceof Expression) {
|
||||
ExpressionEvaluatingSelector selector = new ExpressionEvaluatingSelector((Expression) recipientSelector);
|
||||
selector.setBeanFactory(getBeanFactory());
|
||||
return selector;
|
||||
}
|
||||
else if (recipientSelector instanceof MessageSelector) {
|
||||
return (MessageSelector) recipientSelector;
|
||||
}
|
||||
else if (recipientSelector instanceof GenericSelector) {
|
||||
return new MethodInvokingSelector(new LambdaMessageProcessor(recipientSelector, null));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class DslRecipient extends Recipient {
|
||||
|
||||
private volatile MessageChannel channel;
|
||||
|
||||
DslRecipient(MessageChannelReference channel, MessageSelector selector) {
|
||||
super(channel, selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel getChannel() {
|
||||
if (this.channel == null) {
|
||||
synchronized (this) {
|
||||
if (this.channel == null) {
|
||||
this.channel = resolveChannelName((MessageChannelReference) super.getChannel());
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
private MessageChannel resolveChannelName(MessageChannelReference channelReference) {
|
||||
String channelName = channelReference.getName();
|
||||
try {
|
||||
return DslRecipientListRouter.this.getBeanFactory().getBean(channelName, MessageChannel.class);
|
||||
}
|
||||
catch (BeansException e) {
|
||||
throw new DestinationResolutionException("Failed to look up MessageChannel with name '"
|
||||
+ channelName + "' in the BeanFactory.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.integration.endpoint.AbstractPollingEndpoint;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for endpoints.
|
||||
*
|
||||
* @param <S> the target {@link ConsumerEndpointSpec} implementation type.
|
||||
* @param <F> the target {@link BeanNameAware} implementation type.
|
||||
* @param <H> the target {@link MessageHandler} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class EndpointSpec<S extends EndpointSpec<S, F, H>, F extends BeanNameAware, H>
|
||||
extends IntegrationComponentSpec<S, Tuple2<F, H>>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
protected final Collection<Object> componentToRegister = new ArrayList<Object>();
|
||||
|
||||
protected H handler;
|
||||
|
||||
protected F endpointFactoryBean;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected EndpointSpec(H handler) {
|
||||
try {
|
||||
Class<?> fClass = ResolvableType.forClass(this.getClass()).as(EndpointSpec.class).resolveGenerics()[1];
|
||||
this.endpointFactoryBean = (F) fClass.newInstance();
|
||||
this.handler = handler;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public S id(String id) {
|
||||
this.endpointFactoryBean.setBeanName(id);
|
||||
return super.id(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pollers the pollers
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractPollingEndpoint
|
||||
* @see PollerFactory
|
||||
*/
|
||||
public S poller(Function<PollerFactory, PollerSpec> pollers) {
|
||||
return poller(pollers.apply(new PollerFactory()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pollerMetadataSpec the pollerMetadataSpec
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractPollingEndpoint
|
||||
* @see PollerSpec
|
||||
*/
|
||||
public S poller(PollerSpec pollerMetadataSpec) {
|
||||
Collection<Object> componentsToRegister = pollerMetadataSpec.getComponentsToRegister();
|
||||
if (componentsToRegister != null) {
|
||||
this.componentToRegister.addAll(componentsToRegister);
|
||||
}
|
||||
return poller(pollerMetadataSpec.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pollerMetadata the pollerMetadata
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractPollingEndpoint
|
||||
*/
|
||||
public abstract S poller(PollerMetadata pollerMetadata);
|
||||
|
||||
/**
|
||||
* @param phase the phase.
|
||||
* @return the endpoint spec.
|
||||
* @see SmartLifecycle
|
||||
*/
|
||||
public abstract S phase(int phase);
|
||||
|
||||
/**
|
||||
* @param autoStartup the autoStartup.
|
||||
* @return the endpoint spec
|
||||
* @see SmartLifecycle
|
||||
*/
|
||||
public abstract S autoStartup(boolean autoStartup);
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return this.componentToRegister.isEmpty()
|
||||
? null
|
||||
: this.componentToRegister;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Tuple2<F, H> doGet() {
|
||||
return Tuples.of(this.endpointFactoryBean, this.handler);
|
||||
}
|
||||
|
||||
protected void assertHandler() {
|
||||
Assert.state(this.handler != null, "'this.handler' must not be null.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.expression.ValueExpression;
|
||||
import org.springframework.integration.transformer.ContentEnricher;
|
||||
import org.springframework.integration.transformer.support.AbstractHeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.HeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.StaticHeaderValueMessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link MessageHandlerSpec} implementation for the {@link ContentEnricher}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Tim Ysewyn
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class EnricherSpec extends MessageHandlerSpec<EnricherSpec, ContentEnricher> {
|
||||
|
||||
private final ContentEnricher enricher = new ContentEnricher();
|
||||
|
||||
private final Map<String, Expression> propertyExpressions = new HashMap<String, Expression>();
|
||||
|
||||
private final Map<String, HeaderValueMessageProcessor<?>> headerExpressions =
|
||||
new HashMap<String, HeaderValueMessageProcessor<?>>();
|
||||
|
||||
EnricherSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestChannel the request channel.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setRequestChannel(MessageChannel)
|
||||
*/
|
||||
public EnricherSpec requestChannel(MessageChannel requestChannel) {
|
||||
this.enricher.setRequestChannel(requestChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestChannel the request channel.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setRequestChannelName(String)
|
||||
*/
|
||||
public EnricherSpec requestChannel(String requestChannel) {
|
||||
this.enricher.setRequestChannelName(requestChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param replyChannel the reply channel.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setReplyChannel(MessageChannel)
|
||||
*/
|
||||
public EnricherSpec replyChannel(MessageChannel replyChannel) {
|
||||
this.enricher.setReplyChannel(replyChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param replyChannel the reply channel.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setReplyChannelName(String)
|
||||
*/
|
||||
public EnricherSpec replyChannel(String replyChannel) {
|
||||
this.enricher.setReplyChannelName(replyChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestTimeout the requestTimeout
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setRequestTimeout(Long)
|
||||
*/
|
||||
public EnricherSpec requestTimeout(Long requestTimeout) {
|
||||
this.enricher.setRequestTimeout(requestTimeout);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param replyTimeout the replyTimeout
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setReplyTimeout(Long)
|
||||
*/
|
||||
public EnricherSpec replyTimeout(Long replyTimeout) {
|
||||
this.enricher.setReplyTimeout(replyTimeout);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestPayloadExpression the requestPayloadExpression.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setRequestPayloadExpression(Expression)
|
||||
*/
|
||||
public EnricherSpec requestPayloadExpression(String requestPayloadExpression) {
|
||||
this.enricher.setRequestPayloadExpression(PARSER.parseExpression(requestPayloadExpression));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestPayloadFunction the requestPayloadFunction.
|
||||
* @param <P> the payload type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setRequestPayloadExpression(Expression)
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> EnricherSpec requestPayload(Function<Message<P>, ?> requestPayloadFunction) {
|
||||
this.enricher.setRequestPayloadExpression(new FunctionExpression<>(requestPayloadFunction));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shouldClonePayload the shouldClonePayload.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setShouldClonePayload(boolean)
|
||||
*/
|
||||
public EnricherSpec shouldClonePayload(boolean shouldClonePayload) {
|
||||
this.enricher.setShouldClonePayload(shouldClonePayload);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key.
|
||||
* @param value the value.
|
||||
* @param <V> the value type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setPropertyExpressions(Map)
|
||||
*/
|
||||
public <V> EnricherSpec property(String key, V value) {
|
||||
this.propertyExpressions.put(key, new ValueExpression<V>(value));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key.
|
||||
* @param expression the expression.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setPropertyExpressions(Map)
|
||||
*/
|
||||
public EnricherSpec propertyExpression(String key, String expression) {
|
||||
Assert.notNull(key);
|
||||
this.propertyExpressions.put(key, PARSER.parseExpression(expression));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key.
|
||||
* @param function the function (usually a JDK8 lambda).
|
||||
* @param <P> the payload type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setPropertyExpressions(Map)
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> EnricherSpec propertyFunction(String key, Function<Message<P>, Object> function) {
|
||||
this.propertyExpressions.put(key, new FunctionExpression<>(function));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header with the value if it is not already present.
|
||||
* @param name the header name.
|
||||
* @param value the value.
|
||||
* @param <V> the value type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
*/
|
||||
public <V> EnricherSpec header(String name, V value) {
|
||||
return this.header(name, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the header name.
|
||||
* @param value the value.
|
||||
* @param overwrite true to overwrite the header if already present.
|
||||
* @param <V> the value type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
*/
|
||||
public <V> EnricherSpec header(String name, V value, Boolean overwrite) {
|
||||
AbstractHeaderValueMessageProcessor<V> headerValueMessageProcessor =
|
||||
new StaticHeaderValueMessageProcessor<V>(value);
|
||||
headerValueMessageProcessor.setOverwrite(overwrite);
|
||||
return header(name, headerValueMessageProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header with the expression evaluation if the header is not already present.
|
||||
* @param name the header name.
|
||||
* @param expression the expression to be evaluated against the reply message to obtain the value.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
*/
|
||||
public EnricherSpec headerExpression(String name, String expression) {
|
||||
return headerExpression(name, expression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the header name.
|
||||
* @param expression the expression to be evaluated against the reply message to obtain the value.
|
||||
* @param overwrite true to overwrite the header if already present.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
*/
|
||||
public EnricherSpec headerExpression(String name, String expression, Boolean overwrite) {
|
||||
Assert.hasText(expression);
|
||||
return headerExpression(name, PARSER.parseExpression(expression), overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header with the function return value if the header is not already present.
|
||||
* @param name the header name.
|
||||
* @param function the function (usually a JDK8 lambda).
|
||||
* @param <P> the payload type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> EnricherSpec headerFunction(String name, Function<Message<P>, Object> function) {
|
||||
return headerFunction(name, function, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the header name.
|
||||
* @param function the function (usually a JDK8 lambda).
|
||||
* @param overwrite true to overwrite the header if already present.
|
||||
* @param <P> the payload type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> EnricherSpec headerFunction(String name, Function<Message<P>, Object> function, Boolean overwrite) {
|
||||
return headerExpression(name, new FunctionExpression<>(function), overwrite);
|
||||
}
|
||||
|
||||
private EnricherSpec headerExpression(String name, Expression expression, Boolean overwrite) {
|
||||
AbstractHeaderValueMessageProcessor<?> headerValueMessageProcessor =
|
||||
new ExpressionEvaluatingHeaderValueMessageProcessor<>(expression, null);
|
||||
headerValueMessageProcessor.setOverwrite(overwrite);
|
||||
return header(name, headerValueMessageProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a header value using an explicit {@link HeaderValueMessageProcessor}.
|
||||
* @param name the header name.
|
||||
* @param headerValueMessageProcessor the headerValueMessageProcessor.
|
||||
* @param <V> the value type.
|
||||
* @return the enricher spec.
|
||||
* @see ContentEnricher#setHeaderExpressions(Map)
|
||||
*/
|
||||
public <V> EnricherSpec header(String name, HeaderValueMessageProcessor<V> headerValueMessageProcessor) {
|
||||
Assert.hasText(name);
|
||||
this.headerExpressions.put(name, headerValueMessageProcessor);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContentEnricher doGet() {
|
||||
if (!this.propertyExpressions.isEmpty()) {
|
||||
this.enricher.setPropertyExpressions(this.propertyExpressions);
|
||||
}
|
||||
if (!this.headerExpressions.isEmpty()) {
|
||||
this.enricher.setHeaderExpressions(this.headerExpressions);
|
||||
}
|
||||
return this.enricher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ConsumerEndpointSpec} implementation for the {@link MessageFilter}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class FilterEndpointSpec extends ConsumerEndpointSpec<FilterEndpointSpec, MessageFilter> {
|
||||
|
||||
FilterEndpointSpec(MessageFilter messageFilter) {
|
||||
super(messageFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value is <code>false</code> meaning that rejected
|
||||
* Messages will be quietly dropped or sent to the discard channel if
|
||||
* available. Typically this value would not be <code>true</code> when
|
||||
* a discard channel is provided, but if so, it will still apply
|
||||
* (in such a case, the Message will be sent to the discard channel,
|
||||
* and <em>then</em> the exception will be thrown).
|
||||
* @param throwExceptionOnRejection the throwExceptionOnRejection.
|
||||
* @return the endpoint spec.
|
||||
* @see MessageFilter#setThrowExceptionOnRejection(boolean)
|
||||
*/
|
||||
public FilterEndpointSpec throwExceptionOnRejection(boolean throwExceptionOnRejection) {
|
||||
this.handler.setThrowExceptionOnRejection(throwExceptionOnRejection);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a channel where rejected Messages should be sent. If the discard
|
||||
* channel is null (the default), rejected Messages will be dropped. However,
|
||||
* the 'throwExceptionOnRejection' flag determines whether rejected Messages
|
||||
* trigger an exception. That value is evaluated regardless of the presence
|
||||
* of a discard channel.
|
||||
* @param discardChannel the discardChannel.
|
||||
* @return the endpoint spec.
|
||||
* @see MessageFilter#setDiscardChannel(MessageChannel)
|
||||
*/
|
||||
public FilterEndpointSpec discardChannel(MessageChannel discardChannel) {
|
||||
this.handler.setDiscardChannel(discardChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a channel name where rejected Messages should be sent. If the discard
|
||||
* channel is null (the default), rejected Messages will be dropped. However,
|
||||
* the 'throwExceptionOnRejection' flag determines whether rejected Messages
|
||||
* trigger an exception. That value is evaluated regardless of the presence
|
||||
* of a discard channel.
|
||||
* @param discardChannelName the discardChannelName.
|
||||
* @return the endpoint spec.
|
||||
* @see MessageFilter#setDiscardChannelName(String)
|
||||
*/
|
||||
public FilterEndpointSpec discardChannel(String discardChannelName) {
|
||||
this.handler.setDiscardChannelName(discardChannelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a subflow to run for discarded messages instead of a
|
||||
* {@link #discardChannel(MessageChannel)}.
|
||||
* @param discardFlow the discard flow.
|
||||
* @return the endpoint spec.
|
||||
*/
|
||||
public FilterEndpointSpec discardFlow(IntegrationFlow discardFlow) {
|
||||
Assert.notNull(discardFlow);
|
||||
DirectChannel channel = new DirectChannel();
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(channel);
|
||||
discardFlow.configure(flowBuilder);
|
||||
this.componentToRegister.add(flowBuilder.get());
|
||||
return discardChannel(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to 'true' if you wish the discard processing to occur within any
|
||||
* request handler advice applied to this filter. Also applies to
|
||||
* throwing an exception on rejection. Default: true.
|
||||
* @param discardWithinAdvice the discardWithinAdvice.
|
||||
* @return the endpoint spec.
|
||||
* @see MessageFilter#setDiscardWithinAdvice(boolean)
|
||||
*/
|
||||
public FilterEndpointSpec discardWithinAdvice(boolean discardWithinAdvice) {
|
||||
this.handler.setDiscardWithinAdvice(discardWithinAdvice);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* A {@link ConsumerEndpointSpec} implementation for a mid-flow {@link GatewayMessageHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class GatewayEndpointSpec extends ConsumerEndpointSpec<GatewayEndpointSpec, GatewayMessageHandler> {
|
||||
|
||||
GatewayEndpointSpec(MessageChannel requestChannel) {
|
||||
super(new GatewayMessageHandler());
|
||||
this.handler.setRequestChannel(requestChannel);
|
||||
}
|
||||
|
||||
GatewayEndpointSpec(String requestChannel) {
|
||||
super(new GatewayMessageHandler());
|
||||
this.handler.setRequestChannelName(requestChannel);
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec replyChannel(MessageChannel replyChannel) {
|
||||
this.handler.setReplyChannel(replyChannel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec replyChannel(String replyChannel) {
|
||||
this.handler.setReplyChannelName(replyChannel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec errorChannel(MessageChannel errorChannel) {
|
||||
this.handler.setErrorChannel(errorChannel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec errorChannel(String errorChannel) {
|
||||
this.handler.setErrorChannelName(errorChannel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec requestTimeout(Long requestTimeout) {
|
||||
this.handler.setRequestTimeout(requestTimeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayEndpointSpec replyTimeout(Long replyTimeout) {
|
||||
this.handler.setReplyTimeout(replyTimeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
|
||||
import org.springframework.integration.gateway.RequestReplyExchanger;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
class GatewayMessageHandler extends AbstractReplyProducingMessageHandler implements Lifecycle {
|
||||
|
||||
private final GatewayProxyFactoryBean gatewayProxyFactoryBean;
|
||||
|
||||
private RequestReplyExchanger exchanger;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
GatewayMessageHandler() {
|
||||
this.gatewayProxyFactoryBean = new GatewayProxyFactoryBean();
|
||||
this.gatewayProxyFactoryBean.setServiceInterface(RequestReplyExchanger.class);
|
||||
}
|
||||
|
||||
void setRequestChannel(MessageChannel requestChannel) {
|
||||
this.gatewayProxyFactoryBean.setDefaultRequestChannel(requestChannel);
|
||||
}
|
||||
|
||||
void setRequestChannelName(String requestChannel) {
|
||||
this.gatewayProxyFactoryBean.setDefaultRequestChannelName(requestChannel);
|
||||
}
|
||||
|
||||
public void setReplyChannel(MessageChannel replyChannel) {
|
||||
this.gatewayProxyFactoryBean.setDefaultReplyChannel(replyChannel);
|
||||
}
|
||||
|
||||
public void setReplyChannelName(String replyChannel) {
|
||||
this.gatewayProxyFactoryBean.setDefaultReplyChannelName(replyChannel);
|
||||
}
|
||||
|
||||
public void setErrorChannel(MessageChannel errorChannel) {
|
||||
this.gatewayProxyFactoryBean.setErrorChannel(errorChannel);
|
||||
}
|
||||
|
||||
public void setErrorChannelName(String errorChannel) {
|
||||
this.gatewayProxyFactoryBean.setErrorChannelName(errorChannel);
|
||||
}
|
||||
|
||||
public void setRequestTimeout(Long requestTimeout) {
|
||||
this.gatewayProxyFactoryBean.setDefaultRequestTimeout(requestTimeout);
|
||||
}
|
||||
|
||||
public void setReplyTimeout(Long replyTimeout) {
|
||||
this.gatewayProxyFactoryBean.setDefaultReplyTimeout(replyTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (this.exchanger == null) {
|
||||
synchronized (this) {
|
||||
if (this.exchanger == null) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.exchanger.exchange(requestMessage);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
((ConfigurableListableBeanFactory) beanFactory).initializeBean(this.gatewayProxyFactoryBean, null);
|
||||
}
|
||||
try {
|
||||
this.exchanger = (RequestReplyExchanger) this.gatewayProxyFactoryBean.getObject();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new BeanCreationException("Can't instantiate the GatewayProxyFactoryBean: " + this, e);
|
||||
}
|
||||
if (this.running) {
|
||||
// We must stop gatewayProxyFactoryBean because after the normal start its "gatewayMap" is still empty
|
||||
this.gatewayProxyFactoryBean.stop();
|
||||
this.gatewayProxyFactoryBean.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.gatewayProxyFactoryBean.start();
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.gatewayProxyFactoryBean.stop();
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
|
||||
/**
|
||||
* A {@link ConsumerEndpointSpec} for a {@link MessageHandler} implementations.
|
||||
*
|
||||
* @param <H> the {@link MessageHandler} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class GenericEndpointSpec<H extends MessageHandler>
|
||||
extends ConsumerEndpointSpec<GenericEndpointSpec<H>, H> {
|
||||
|
||||
GenericEndpointSpec(H messageHandler) {
|
||||
super(messageHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.handler.BeanNameMessageProcessor;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.support.MapBuilder;
|
||||
import org.springframework.integration.support.StringStringMapBuilder;
|
||||
import org.springframework.integration.transformer.HeaderEnricher;
|
||||
import org.springframework.integration.transformer.support.AbstractHeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.HeaderValueMessageProcessor;
|
||||
import org.springframework.integration.transformer.support.StaticHeaderValueMessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for a {@link HeaderEnricher}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HeaderEnricherSpec extends IntegrationComponentSpec<HeaderEnricherSpec, HeaderEnricher> {
|
||||
|
||||
private final Map<String, HeaderValueMessageProcessor<?>> headerToAdd = new HashMap<>();
|
||||
|
||||
private boolean defaultOverwrite = false;
|
||||
|
||||
private boolean shouldSkipNulls = true;
|
||||
|
||||
private MessageProcessor<?> messageProcessor;
|
||||
|
||||
HeaderEnricherSpec() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the default action to take when setting individual header specifications
|
||||
* without an explicit 'overwrite' argument.
|
||||
* @param defaultOverwrite the defaultOverwrite.
|
||||
* @return the header enricher spec.
|
||||
* @see HeaderEnricher#setDefaultOverwrite(boolean)
|
||||
*/
|
||||
public HeaderEnricherSpec defaultOverwrite(boolean defaultOverwrite) {
|
||||
this.defaultOverwrite = defaultOverwrite;
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shouldSkipNulls the shouldSkipNulls.
|
||||
* @return the header enricher spec.
|
||||
* @see HeaderEnricher#setShouldSkipNulls(boolean)
|
||||
*/
|
||||
public HeaderEnricherSpec shouldSkipNulls(boolean shouldSkipNulls) {
|
||||
this.shouldSkipNulls = shouldSkipNulls;
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an optional custom {@link MessageProcessor} for the enricher. The
|
||||
* processor must return a {@link Map} of header names and values. They will be added
|
||||
* to the inbound message headers before evaluating the individual configured header
|
||||
* specifications.
|
||||
* @param messageProcessor the messageProcessor.
|
||||
* @return the header enricher spec.
|
||||
* @see HeaderEnricher#setMessageProcessor(MessageProcessor)
|
||||
*/
|
||||
public HeaderEnricherSpec messageProcessor(MessageProcessor<?> messageProcessor) {
|
||||
this.messageProcessor = messageProcessor;
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link ExpressionEvaluatingMessageProcessor} that evaluates to a
|
||||
* {@link Map} of additional headers. They will be added to the inbound message
|
||||
* headers before evaluating the individual configured header specifications.
|
||||
* @param expression the expression.
|
||||
* @return the header enricher spec.
|
||||
* @see #messageProcessor(MessageProcessor)
|
||||
*/
|
||||
public HeaderEnricherSpec messageProcessor(String expression) {
|
||||
return messageProcessor(new ExpressionEvaluatingMessageProcessor<Object>(PARSER.parseExpression(expression)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an
|
||||
* {@link org.springframework.integration.handler.MethodInvokingMessageProcessor} that
|
||||
* invokes the method on the bean - the method must return a {@link Map} of headers.
|
||||
* They will be added to the inbound message headers before evaluating the individual
|
||||
* configured header specifications.
|
||||
* @param beanName The bean name.
|
||||
* @param methodName The method name.
|
||||
* @return the header enricher spec.
|
||||
* @see #messageProcessor(MessageProcessor)
|
||||
*/
|
||||
public HeaderEnricherSpec messageProcessor(String beanName, String methodName) {
|
||||
return messageProcessor(new BeanNameMessageProcessor<Object>(beanName, methodName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link MapBuilder}; if a map value is an
|
||||
* {@link Expression}, it will be evaluated at run time when the message headers are
|
||||
* enriched. Otherwise the value is simply added to the headers. Headers derived from
|
||||
* the map will <b>not</b> overwrite existing headers, unless
|
||||
* {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param headers the header map builder.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headers(MapBuilder<?, String, Object> headers) {
|
||||
return headers(headers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link MapBuilder}; if a map value is an
|
||||
* {@link Expression}, it will be evaluated at run time when the message headers are
|
||||
* enriched. Otherwise the value is simply added to the headers.
|
||||
* @param headers the header map builder.
|
||||
* @param overwrite true to overwrite existing headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headers(MapBuilder<?, String, Object> headers, Boolean overwrite) {
|
||||
Assert.notNull(headers);
|
||||
return headers(headers.get(), overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link Map}; if a map value is an
|
||||
* {@link Expression}, it will be evaluated at run time when the message headers are
|
||||
* enriched. Otherwise the value is simply added to the headers. Headers derived from
|
||||
* the map will <em>not</em> overwrite existing headers, unless
|
||||
* {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param headers The header builder.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headers(Map<String, Object> headers) {
|
||||
return headers(headers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link Map}; if a map value is an
|
||||
* {@link Expression}, it will be evaluated at run time when the message headers are
|
||||
* enriched. Otherwise the value is simply added to the headers.
|
||||
* @param headers The header builder.
|
||||
* @param overwrite true to overwrite existing headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headers(Map<String, Object> headers, Boolean overwrite) {
|
||||
Assert.notNull(headers);
|
||||
for (Entry<String, Object> entry : headers.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Expression) {
|
||||
AbstractHeaderValueMessageProcessor<Object> processor =
|
||||
new ExpressionEvaluatingHeaderValueMessageProcessor<Object>((Expression) value, null);
|
||||
processor.setOverwrite(overwrite);
|
||||
header(name, processor);
|
||||
}
|
||||
else {
|
||||
header(name, value, overwrite);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link MapBuilder}; the {@link Map} values must
|
||||
* be String representations of SpEL expressions that will be evaluated at run time
|
||||
* when the message headers are enriched. Headers derived from the map will <b>not</b>
|
||||
* overwrite existing headers, unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param headers the header map builder.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(MapBuilder<?, String, String> headers) {
|
||||
return headerExpressions(headers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications from the {@link MapBuilder}; the {@link Map} values must
|
||||
* be String representations of SpEL expressions that will be evaluated at run time
|
||||
* when the message headers are enriched.
|
||||
* @param headers the header map builder.
|
||||
* @param overwrite true to overwrite existing headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(MapBuilder<?, String, String> headers, Boolean overwrite) {
|
||||
Assert.notNull(headers);
|
||||
return headerExpressions(headers.get(), overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications via the consumer callback, which receives a
|
||||
* {@link StringStringMapBuilder}; the {@link Map} values must be String
|
||||
* representations of SpEL expressions that will be evaluated at run time when the
|
||||
* message headers are enriched. Headers derived from the map will <b>not</b>
|
||||
* overwrite existing headers, unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* Usually used with a JDK8 lambda:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* .enrichHeaders(s -> s.headerExpressions(c -> c
|
||||
* .put(MailHeaders.SUBJECT, "payload.subject")
|
||||
* .put(MailHeaders.FROM, "payload.from[0].toString()")))
|
||||
* }
|
||||
* </pre>
|
||||
* @param configurer the configurer.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(Consumer<StringStringMapBuilder> configurer) {
|
||||
return headerExpressions(configurer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications via the consumer callback, which receives a
|
||||
* {@link StringStringMapBuilder}; the {@link Map} values must be String
|
||||
* representations of SpEL expressions that will be evaluated at run time when the
|
||||
* message headers are enriched. Usually used with a JDK8 lambda:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* .enrichHeaders(s -> s.headerExpressions(c -> c
|
||||
* .put(MailHeaders.SUBJECT, "payload.subject")
|
||||
* .put(MailHeaders.FROM, "payload.from[0].toString()"), true))
|
||||
* }
|
||||
* </pre>
|
||||
* @param configurer the configurer.
|
||||
* @param overwrite true to overwrite existing headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(Consumer<StringStringMapBuilder> configurer, Boolean overwrite) {
|
||||
Assert.notNull(configurer);
|
||||
StringStringMapBuilder builder = new StringStringMapBuilder();
|
||||
configurer.accept(builder);
|
||||
return headerExpressions(builder.get(), overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications; the {@link Map} values must be String representations
|
||||
* of SpEL expressions that will be evaluated at run time when the message headers are
|
||||
* enriched. Headers derived from the map will <b>not</b> overwrite existing headers,
|
||||
* unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param headers the headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(Map<String, String> headers) {
|
||||
return headerExpressions(headers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications; the {@link Map} values must be String representations of
|
||||
* SpEL expressions that will be evaluated at run time when the message headers are
|
||||
* enriched.
|
||||
* @param headers the headers.
|
||||
* @param overwrite true to overwrite existing headers.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpressions(Map<String, String> headers, Boolean overwrite) {
|
||||
Assert.notNull(headers);
|
||||
for (Entry<String, String> entry : headers.entrySet()) {
|
||||
AbstractHeaderValueMessageProcessor<Object> processor =
|
||||
new ExpressionEvaluatingHeaderValueMessageProcessor<Object>(entry.getValue(), null);
|
||||
processor.setOverwrite(overwrite);
|
||||
header(entry.getKey(), processor);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification. If the header exists, it will <b>not</b> be
|
||||
* overwritten unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param name the header name.
|
||||
* @param value the header value (not an {@link Expression}).
|
||||
* @param <V> the value type.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public <V> HeaderEnricherSpec header(String name, V value) {
|
||||
return header(name, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification.
|
||||
* @param name the header name.
|
||||
* @param value the header value (not an {@link Expression}).
|
||||
* @param overwrite true to overwrite an existing header.
|
||||
* @param <V> the value type.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public <V> HeaderEnricherSpec header(String name, V value, Boolean overwrite) {
|
||||
AbstractHeaderValueMessageProcessor<V> headerValueMessageProcessor =
|
||||
new StaticHeaderValueMessageProcessor<V>(value);
|
||||
headerValueMessageProcessor.setOverwrite(overwrite);
|
||||
return header(name, headerValueMessageProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification where the value is a String representation of a
|
||||
* SpEL {@link Expression}. If the header exists, it will <b>not</b> be overwritten
|
||||
* unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param name the header name.
|
||||
* @param expression the expression.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpression(String name, String expression) {
|
||||
return headerExpression(name, expression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification where the value is a String representation of a
|
||||
* SpEL {@link Expression}.
|
||||
* @param name the header name.
|
||||
* @param expression the expression.
|
||||
* @param overwrite true to overwrite an existing header.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public HeaderEnricherSpec headerExpression(String name, String expression, Boolean overwrite) {
|
||||
Assert.hasText(expression);
|
||||
return headerExpression(name, PARSER.parseExpression(expression), overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification where the value is obtained by invoking the
|
||||
* {@link Function} callback. If the header exists, it will <b>not</b> be overwritten
|
||||
* unless {@link #defaultOverwrite(boolean)} is true.
|
||||
* @param name the header name.
|
||||
* @param function the function.
|
||||
* @param <P> the payload type.
|
||||
* @return the header enricher spec.
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> HeaderEnricherSpec headerFunction(String name, Function<Message<P>, Object> function) {
|
||||
return headerFunction(name, function, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification where the value is obtained by invoking the
|
||||
* {@link Function} callback.
|
||||
* @param name the header name.
|
||||
* @param function the function.
|
||||
* @param overwrite true to overwrite an existing header.
|
||||
* @param <P> the payload type.
|
||||
* @return the header enricher spec.
|
||||
* @see FunctionExpression
|
||||
*/
|
||||
public <P> HeaderEnricherSpec headerFunction(String name, Function<Message<P>, Object> function,
|
||||
Boolean overwrite) {
|
||||
return headerExpression(name, new FunctionExpression<>(function), overwrite);
|
||||
}
|
||||
|
||||
private HeaderEnricherSpec headerExpression(String name, Expression expression, Boolean overwrite) {
|
||||
AbstractHeaderValueMessageProcessor<?> headerValueMessageProcessor =
|
||||
new ExpressionEvaluatingHeaderValueMessageProcessor<Object>(expression, null);
|
||||
headerValueMessageProcessor.setOverwrite(overwrite);
|
||||
return header(name, headerValueMessageProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single header specification where the value is obtained by calling the
|
||||
* {@link HeaderValueMessageProcessor}.
|
||||
* @param name the header name.
|
||||
* @param headerValueMessageProcessor the message processor.
|
||||
* @param <V> the value type.
|
||||
* @return the header enricher spec.
|
||||
*/
|
||||
public <V> HeaderEnricherSpec header(String name, HeaderValueMessageProcessor<V> headerValueMessageProcessor) {
|
||||
Assert.hasText(name);
|
||||
this.headerToAdd.put(name, headerValueMessageProcessor);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications to automatically convert header channels (reply, error
|
||||
* channels) to Strings and store them in a header channel registry. Allows
|
||||
* persistence and serialization of messages without losing these important framework
|
||||
* headers.
|
||||
* @return the header enricher spec.
|
||||
* @see org.springframework.integration.support.channel.HeaderChannelRegistry
|
||||
*/
|
||||
public HeaderEnricherSpec headerChannelsToString() {
|
||||
return headerChannelsToString(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add header specifications to automatically convert header channels (reply, error
|
||||
* channels) to Strings and store them in a header channel registry. Allows
|
||||
* persistence and serialization of messages without losing these important framework
|
||||
* headers.
|
||||
* @param timeToLiveExpression the minimum time that the mapping will remain in the registry.
|
||||
* @return the header enricher spec.
|
||||
* @see org.springframework.integration.support.channel.HeaderChannelRegistry
|
||||
*/
|
||||
public HeaderEnricherSpec headerChannelsToString(String timeToLiveExpression) {
|
||||
return headerExpression("replyChannel",
|
||||
"@" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME
|
||||
+ ".channelToChannelName(headers.replyChannel" +
|
||||
(StringUtils.hasText(timeToLiveExpression) ? ", " + timeToLiveExpression : "") + ")",
|
||||
true)
|
||||
.headerExpression("errorChannel",
|
||||
"@" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME
|
||||
+ ".channelToChannelName(headers.errorChannel" +
|
||||
(StringUtils.hasText(timeToLiveExpression) ? ", " + timeToLiveExpression : "") + ")",
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HeaderEnricher doGet() {
|
||||
HeaderEnricher headerEnricher = new HeaderEnricher(new HashMap<>(this.headerToAdd));
|
||||
headerEnricher.setDefaultOverwrite(this.defaultOverwrite);
|
||||
headerEnricher.setShouldSkipNulls(this.shouldSkipNulls);
|
||||
headerEnricher.setMessageProcessor(this.messageProcessor);
|
||||
return headerEnricher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* The common Builder abstraction. The {@link #get()} method returns the final component.
|
||||
*
|
||||
* @param <S> the target {@link IntegrationComponentSpec} implementation type.
|
||||
* @param <T> the target type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class IntegrationComponentSpec<S extends IntegrationComponentSpec<S, T>, T>
|
||||
implements FactoryBean<T> {
|
||||
|
||||
protected final static SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected volatile T target;
|
||||
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Configure the component identifier. Used as the {@code beanName} to register the
|
||||
* bean in the application context for this component.
|
||||
* @param id the id.
|
||||
* @return the spec.
|
||||
*/
|
||||
protected S id(String id) {
|
||||
this.id = id;
|
||||
return _this();
|
||||
}
|
||||
|
||||
public final String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configured component.
|
||||
*/
|
||||
public final T get() {
|
||||
if (this.target == null) {
|
||||
this.target = doGet();
|
||||
}
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
return get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return get().getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final S _this() {
|
||||
return (S) this;
|
||||
}
|
||||
|
||||
protected T doGet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
/**
|
||||
* The main Integration DSL abstraction.
|
||||
* <p>
|
||||
* The {@link StandardIntegrationFlow} implementation (produced by {@link IntegrationFlowBuilder})
|
||||
* represents a container for the integration components, which will be registered
|
||||
* in the application context. Typically is used as {@code @Bean} definition:
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* public IntegrationFlow fileReadingFlow() {
|
||||
* return IntegrationFlows
|
||||
* .from(Files.inboundAdapter(tmpDir.getRoot()), e -> e.poller(Pollers.fixedDelay(100)))
|
||||
* .transform(Files.fileToString())
|
||||
* .channel(MessageChannels.queue("fileReadingResultChannel"))
|
||||
* .get();
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* Can be used as a Lambda for top level definition as well as sub-flow definitions:
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* public IntegrationFlow routerTwoSubFlows() {
|
||||
* return f -> f
|
||||
* .split()
|
||||
* .<Integer, Boolean>route(p -> p % 2 == 0, m -> m
|
||||
* .subFlowMapping(true, sf -> sf.<Integer>handle((p, h) -> p * 2))
|
||||
* .subFlowMapping(false, sf -> sf.<Integer>handle((p, h) -> p * 3)))
|
||||
* .aggregate()
|
||||
* .channel(MessageChannels..queue("routerTwoSubFlowsOutput"));
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
* <p>
|
||||
* Also this interface can be implemented directly to encapsulate the integration logic
|
||||
* in the target service:
|
||||
* <pre class="code">
|
||||
* @Component
|
||||
* public class MyFlow implements IntegrationFlow {
|
||||
*
|
||||
* @Override
|
||||
* public void configure(IntegrationFlowDefinition<?> f) {
|
||||
* f.<String, String>transform(String::toUpperCase);
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see IntegrationFlowBuilder
|
||||
* @see StandardIntegrationFlow
|
||||
* @see IntegrationFlowAdapter
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IntegrationFlow {
|
||||
|
||||
void configure(IntegrationFlowDefinition<?> flow);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.dsl.channel.MessageChannelSpec;
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The base {@code Adapter} class for the {@link IntegrationFlow} abstraction.
|
||||
* Requires the implementation for the {@link #buildFlow()} method to produce
|
||||
* {@link IntegrationFlowDefinition} using one of {@link #from} support methods.
|
||||
* <p>
|
||||
* Typically is used for target service implementation:
|
||||
* <pre class="code">
|
||||
* @Component
|
||||
* public class MyFlowAdapter extends IntegrationFlowAdapter {
|
||||
*
|
||||
* @Autowired
|
||||
* private ConnectionFactory rabbitConnectionFactory;
|
||||
*
|
||||
* @Override
|
||||
* protected IntegrationFlowDefinition<?> buildFlow() {
|
||||
* return from(Amqp.inboundAdapter(this.rabbitConnectionFactory, "myQueue"))
|
||||
* .<String, String>transform(String::toLowerCase)
|
||||
* .channel(c -> c.queue("myFlowAdapterOutput"));
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class IntegrationFlowAdapter implements IntegrationFlow, SmartLifecycle {
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean();
|
||||
|
||||
private StandardIntegrationFlow targetIntegrationFlow;
|
||||
|
||||
@Override
|
||||
public final void configure(IntegrationFlowDefinition<?> flow) {
|
||||
IntegrationFlowDefinition<?> targetFlow = buildFlow();
|
||||
Assert.state(targetFlow != null, "the 'buildFlow()' must not return null");
|
||||
flow.integrationComponents.clear();
|
||||
flow.integrationComponents.addAll(targetFlow.integrationComponents);
|
||||
this.targetIntegrationFlow = flow.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
assertTargetIntegrationFlow();
|
||||
if (!this.running.getAndSet(true)) {
|
||||
this.targetIntegrationFlow.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTargetIntegrationFlow() {
|
||||
Assert.state(this.targetIntegrationFlow != null,
|
||||
this + " hasn't been initialized properly via BeanFactory.\n"
|
||||
+ "Missed @EnableIntegration ?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
assertTargetIntegrationFlow();
|
||||
if (this.running.getAndSet(false)) {
|
||||
this.targetIntegrationFlow.stop(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
assertTargetIntegrationFlow();
|
||||
if (this.running.getAndSet(false)) {
|
||||
this.targetIntegrationFlow.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(String messageChannelName) {
|
||||
return IntegrationFlows.from(messageChannelName);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageChannel messageChannel) {
|
||||
return IntegrationFlows.from(messageChannel);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(String messageChannelName, boolean fixedSubscriber) {
|
||||
return IntegrationFlows.from(messageChannelName, fixedSubscriber);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageSourceSpec<?, ? extends MessageSource<?>> messageSourceSpec,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
return IntegrationFlows.from(messageSourceSpec, endpointConfigurer);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageSource<?> messageSource,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
return IntegrationFlows.from(messageSource, endpointConfigurer);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageProducerSupport messageProducer) {
|
||||
return IntegrationFlows.from(messageProducer);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageSource<?> messageSource) {
|
||||
return IntegrationFlows.from(messageSource);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessagingGatewaySupport inboundGateway) {
|
||||
return IntegrationFlows.from(inboundGateway);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageChannelSpec<?, ?> messageChannelSpec) {
|
||||
return IntegrationFlows.from(messageChannelSpec);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageProducerSpec<?, ?> messageProducerSpec) {
|
||||
return IntegrationFlows.from(messageProducerSpec);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessageSourceSpec<?, ? extends MessageSource<?>> messageSourceSpec) {
|
||||
return IntegrationFlows.from(messageSourceSpec);
|
||||
}
|
||||
|
||||
protected IntegrationFlowDefinition<?> from(MessagingGatewaySpec<?, ?> inboundGatewaySpec) {
|
||||
return IntegrationFlows.from(inboundGatewaySpec);
|
||||
}
|
||||
|
||||
protected IntegrationFlowBuilder from(Object service, String methodName) {
|
||||
return IntegrationFlows.from(service, methodName);
|
||||
}
|
||||
|
||||
protected IntegrationFlowBuilder from(Object service, String methodName,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
return IntegrationFlows.from(service, methodName, endpointConfigurer);
|
||||
}
|
||||
|
||||
protected abstract IntegrationFlowDefinition<?> buildFlow();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class IntegrationFlowBuilder extends IntegrationFlowDefinition<IntegrationFlowBuilder> {
|
||||
|
||||
IntegrationFlowBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardIntegrationFlow get() {
|
||||
return super.get();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.dsl.channel.MessageChannelSpec;
|
||||
import org.springframework.integration.dsl.support.FixedSubscriberChannelPrototype;
|
||||
import org.springframework.integration.dsl.support.MessageChannelReference;
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.integration.endpoint.MethodInvokingMessageSource;
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The central factory for fluent {@link IntegrationFlowBuilder} API.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see org.springframework.integration.config.dsl.IntegrationFlowBeanPostProcessor
|
||||
*/
|
||||
public final class IntegrationFlows {
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageChannel} name to the new {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code inputChannel}.
|
||||
* @param messageChannelName the name of existing {@link MessageChannel} bean.
|
||||
* The new {@link DirectChannel} bean will be created on context startup
|
||||
* if there is no bean with this name.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(String messageChannelName) {
|
||||
return from(new MessageChannelReference(messageChannelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageChannel} name to the new {@link IntegrationFlowBuilder} chain.
|
||||
* Typically for the {@link org.springframework.integration.channel.FixedSubscriberChannel} together
|
||||
* with {@code fixedSubscriber = true}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code inputChannel}.
|
||||
* @param messageChannelName the name for {@link DirectChannel} or
|
||||
* {@link org.springframework.integration.channel.FixedSubscriberChannel}
|
||||
* to be created on context startup, not reference.
|
||||
* The {@link MessageChannel} depends on the {@code fixedSubscriber} boolean argument.
|
||||
* @param fixedSubscriber the boolean flag to determine if result {@link MessageChannel} should
|
||||
* be {@link DirectChannel}, if {@code false} or
|
||||
* {@link org.springframework.integration.channel.FixedSubscriberChannel}, if {@code true}.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see DirectChannel
|
||||
* @see org.springframework.integration.channel.FixedSubscriberChannel
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(String messageChannelName, boolean fixedSubscriber) {
|
||||
return fixedSubscriber
|
||||
? from(new FixedSubscriberChannelPrototype(messageChannelName))
|
||||
: from(messageChannelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageChannel} object to the
|
||||
* {@link IntegrationFlowBuilder} chain using the fluent API from {@link MessageChannelSpec}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code inputChannel}.
|
||||
* @param messageChannelSpec the MessageChannelSpec to populate {@link MessageChannel} instance.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see org.springframework.integration.dsl.channel.MessageChannels
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageChannelSpec<?, ?> messageChannelSpec) {
|
||||
Assert.notNull(messageChannelSpec);
|
||||
return from(messageChannelSpec.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MessageChannel} object to the {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code inputChannel}.
|
||||
* @param messageChannel the {@link MessageChannel} to populate.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageChannel messageChannel) {
|
||||
return new IntegrationFlowBuilder().channel(messageChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageSource} object to the {@link IntegrationFlowBuilder} chain
|
||||
* using the fluent API from the provided {@link MessageSourceSpec}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* @param messageSourceSpec the {@link MessageSourceSpec} to use.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see MessageSourceSpec and its implementations.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageSourceSpec<?, ? extends MessageSource<?>> messageSourceSpec) {
|
||||
return from(messageSourceSpec, (Consumer<SourcePollingChannelAdapterSpec>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageSource} object to the {@link IntegrationFlowBuilder} chain
|
||||
* using the fluent API from the provided {@link MessageSourceSpec}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* @param messageSourceSpec the {@link MessageSourceSpec} to use.
|
||||
* @param endpointConfigurer the {@link Consumer} to provide more options for the
|
||||
* {@link org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean}.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see MessageSourceSpec
|
||||
* @see SourcePollingChannelAdapterSpec
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageSourceSpec<?, ? extends MessageSource<?>> messageSourceSpec,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
Assert.notNull(messageSourceSpec);
|
||||
return from(messageSourceSpec.get(), endpointConfigurer, registerComponents(messageSourceSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MethodInvokingMessageSource} for the method of the provided service.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* @param service the service to use.
|
||||
* @param methodName the method to invoke.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @since 1.1
|
||||
* @see MethodInvokingMessageSource
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(Object service, String methodName) {
|
||||
return from(service, methodName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MethodInvokingMessageSource} for the method of the provided service.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* @param service the service to use.
|
||||
* @param methodName the method to invoke.
|
||||
* @param endpointConfigurer the {@link Consumer} to provide more options for the
|
||||
* {@link org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean}.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @since 1.1
|
||||
* @see MethodInvokingMessageSource
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(Object service, String methodName,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
Assert.notNull(service);
|
||||
Assert.hasText(methodName);
|
||||
MethodInvokingMessageSource messageSource = new MethodInvokingMessageSource();
|
||||
messageSource.setObject(service);
|
||||
messageSource.setMethodName(methodName);
|
||||
return from(messageSource, endpointConfigurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MessageSource} object to the {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* @param messageSource the {@link MessageSource} to populate.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see MessageSource
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageSource<?> messageSource) {
|
||||
return from(messageSource, (Consumer<SourcePollingChannelAdapterSpec>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MessageSource} object to the {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageSource}.
|
||||
* In addition use {@link SourcePollingChannelAdapterSpec} to provide options for the underlying
|
||||
* {@link org.springframework.integration.endpoint.SourcePollingChannelAdapter} endpoint.
|
||||
* @param messageSource the {@link MessageSource} to populate.
|
||||
* @param endpointConfigurer the {@link Consumer} to provide more options for the
|
||||
* {@link org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean}.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see MessageSource
|
||||
* @see SourcePollingChannelAdapterSpec
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageSource<?> messageSource,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer) {
|
||||
return from(messageSource, endpointConfigurer, null);
|
||||
}
|
||||
|
||||
private static IntegrationFlowBuilder from(MessageSource<?> messageSource,
|
||||
Consumer<SourcePollingChannelAdapterSpec> endpointConfigurer,
|
||||
IntegrationFlowBuilder integrationFlowBuilder) {
|
||||
SourcePollingChannelAdapterSpec spec = new SourcePollingChannelAdapterSpec(messageSource);
|
||||
if (endpointConfigurer != null) {
|
||||
endpointConfigurer.accept(spec);
|
||||
}
|
||||
if (integrationFlowBuilder == null) {
|
||||
integrationFlowBuilder = new IntegrationFlowBuilder();
|
||||
}
|
||||
return integrationFlowBuilder.addComponent(spec)
|
||||
.currentComponent(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessageProducerSupport} object to the {@link IntegrationFlowBuilder} chain
|
||||
* using the fluent API from the {@link MessageProducerSpec}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageProducer}.
|
||||
* @param messageProducerSpec the {@link MessageProducerSpec} to use.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
* @see MessageProducerSpec
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageProducerSpec<?, ?> messageProducerSpec) {
|
||||
return from(messageProducerSpec.get(), registerComponents(messageProducerSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MessageProducerSupport} object to the {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageProducer}.
|
||||
* @param messageProducer the {@link MessageProducerSupport} to populate.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessageProducerSupport messageProducer) {
|
||||
return from(messageProducer, (IntegrationFlowBuilder) null);
|
||||
}
|
||||
|
||||
private static IntegrationFlowBuilder from(MessageProducerSupport messageProducer,
|
||||
IntegrationFlowBuilder integrationFlowBuilder) {
|
||||
MessageChannel outputChannel = messageProducer.getOutputChannel();
|
||||
if (outputChannel == null) {
|
||||
outputChannel = new DirectChannel();
|
||||
messageProducer.setOutputChannel(outputChannel);
|
||||
}
|
||||
if (integrationFlowBuilder == null) {
|
||||
integrationFlowBuilder = from(outputChannel);
|
||||
}
|
||||
else {
|
||||
integrationFlowBuilder.channel(outputChannel);
|
||||
}
|
||||
return integrationFlowBuilder.addComponent(messageProducer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@link MessagingGatewaySupport} object to the {@link IntegrationFlowBuilder} chain
|
||||
* using the fluent API from the {@link MessagingGatewaySpec}.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessagingGateway}.
|
||||
* @param inboundGatewaySpec the {@link MessagingGatewaySpec} to use.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessagingGatewaySpec<?, ?> inboundGatewaySpec) {
|
||||
return from(inboundGatewaySpec.get(), registerComponents(inboundGatewaySpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the provided {@link MessagingGatewaySupport} object to the {@link IntegrationFlowBuilder} chain.
|
||||
* The {@link org.springframework.integration.dsl.IntegrationFlow} {@code startMessageProducer}.
|
||||
* @param inboundGateway the {@link MessagingGatewaySupport} to populate.
|
||||
* @return new {@link IntegrationFlowBuilder}.
|
||||
*/
|
||||
public static IntegrationFlowBuilder from(MessagingGatewaySupport inboundGateway) {
|
||||
return from(inboundGateway, (IntegrationFlowBuilder) null);
|
||||
}
|
||||
|
||||
private static IntegrationFlowBuilder from(MessagingGatewaySupport inboundGateway,
|
||||
IntegrationFlowBuilder integrationFlowBuilder) {
|
||||
MessageChannel outputChannel = inboundGateway.getRequestChannel();
|
||||
if (outputChannel == null) {
|
||||
outputChannel = new DirectChannel();
|
||||
inboundGateway.setRequestChannel(outputChannel);
|
||||
}
|
||||
if (integrationFlowBuilder == null) {
|
||||
integrationFlowBuilder = from(outputChannel);
|
||||
}
|
||||
else {
|
||||
integrationFlowBuilder.channel(outputChannel);
|
||||
}
|
||||
return integrationFlowBuilder.addComponent(inboundGateway);
|
||||
}
|
||||
|
||||
private static IntegrationFlowBuilder registerComponents(Object spec) {
|
||||
if (spec instanceof ComponentsRegistration) {
|
||||
return new IntegrationFlowBuilder()
|
||||
.addComponents(((ComponentsRegistration) spec).getComponentsToRegister());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private IntegrationFlows() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFactoryAware {
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final TypeDescriptor payloadType;
|
||||
|
||||
private final Class<?>[] parameterTypes;
|
||||
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
LambdaMessageProcessor(Object target, Class<?> payloadType) {
|
||||
Assert.notNull(target);
|
||||
this.target = target;
|
||||
final AtomicReference<Method> methodValue = new AtomicReference<>();
|
||||
ReflectionUtils.doWithMethods(target.getClass(),
|
||||
methodValue::set,
|
||||
methodCandidate ->
|
||||
!methodCandidate.isBridge()
|
||||
&& !methodCandidate.isDefault()
|
||||
&& methodCandidate.getDeclaringClass() != Object.class
|
||||
&& Modifier.isPublic(methodCandidate.getModifiers())
|
||||
&& !Modifier.isStatic(methodCandidate.getModifiers()));
|
||||
|
||||
Assert.notNull(methodValue.get(), "LambdaMessageProcessor is applicable for inline or lambda " +
|
||||
"classes with single method - functional interface implementations.");
|
||||
|
||||
this.method = methodValue.get();
|
||||
this.method.setAccessible(true);
|
||||
this.parameterTypes = this.method.getParameterTypes();
|
||||
this.payloadType = payloadType != null ? TypeDescriptor.valueOf(payloadType) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
ConversionService conversionService = IntegrationUtils.getConversionService(beanFactory);
|
||||
if (conversionService == null) {
|
||||
conversionService = new DefaultConversionService();
|
||||
}
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object processMessage(Message<?> message) {
|
||||
Object[] args = new Object[this.parameterTypes.length];
|
||||
for (int i = 0; i < this.parameterTypes.length; i++) {
|
||||
Class<?> parameterType = this.parameterTypes[i];
|
||||
if (Message.class.isAssignableFrom(parameterType)) {
|
||||
args[i] = message;
|
||||
}
|
||||
if (Map.class.isAssignableFrom(parameterType)) {
|
||||
if (message.getPayload() instanceof Map) {
|
||||
args[i] = message.getPayload();
|
||||
}
|
||||
else {
|
||||
args[i] = message.getHeaders();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.payloadType != null) {
|
||||
if (Message.class.isAssignableFrom(this.payloadType.getType())) {
|
||||
args[i] = message;
|
||||
}
|
||||
else {
|
||||
args[i] = this.conversionService.convert(message.getPayload(),
|
||||
TypeDescriptor.forObject(message.getPayload()), this.payloadType);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
args[i] = message.getPayload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return this.method.invoke(this.target, args);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new MessageHandlingException(message, e.getCause());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for {@link MessageHandler}s.
|
||||
*
|
||||
* @param <S> the target {@link ConsumerEndpointSpec} implementation type.
|
||||
* @param <H> the target {@link MessageHandler} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessageHandlerSpec<S extends MessageHandlerSpec<S, H>, H extends MessageHandler>
|
||||
extends IntegrationComponentSpec<S, H> {
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
|
||||
/**
|
||||
* The {@link IntegrationComponentSpec} specific base class
|
||||
* for {@link MessageProcessor}s.
|
||||
*
|
||||
* @param <S> the target {@link MessageProcessorSpec} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessageProcessorSpec<S extends MessageProcessorSpec<S>>
|
||||
extends IntegrationComponentSpec<S, MessageProcessor<?>> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.endpoint.MessageProducerSupport;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for
|
||||
* {@link org.springframework.integration.core.MessageProducer}s.
|
||||
*
|
||||
* @param <S> the target {@link MessageProducerSpec} implementation type.
|
||||
* @param <P> the target {@link MessageProducerSupport} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessageProducerSpec<S extends MessageProducerSpec<S, P>, P extends MessageProducerSupport>
|
||||
extends IntegrationComponentSpec<S, P> {
|
||||
|
||||
public MessageProducerSpec(P producer) {
|
||||
this.target = producer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Configure the message producer's bean name.
|
||||
*/
|
||||
@Override
|
||||
public S id(String id) {
|
||||
this.target.setBeanName(id);
|
||||
return super.id(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param phase the phase.
|
||||
* @return the spec.
|
||||
* @see org.springframework.context.SmartLifecycle
|
||||
*/
|
||||
public S phase(int phase) {
|
||||
this.target.setPhase(phase);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param autoStartup the autoStartup.
|
||||
* @return the spec.
|
||||
* @see org.springframework.context.SmartLifecycle
|
||||
*/
|
||||
public S autoStartup(boolean autoStartup) {
|
||||
this.target.setAutoStartup(autoStartup);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@code outputChannel} for the
|
||||
* {@link org.springframework.integration.core.MessageProducer}
|
||||
* @param outputChannel the outputChannel.
|
||||
* @return the spec.
|
||||
* @see MessageProducerSupport#setOutputChannel(MessageChannel)
|
||||
*/
|
||||
public S outputChannel(MessageChannel outputChannel) {
|
||||
target.setOutputChannel(outputChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the bean name of the {@code outputChannel} for the
|
||||
* {@link org.springframework.integration.core.MessageProducer}
|
||||
* @param outputChannel the outputChannel bean name.
|
||||
* @return the spec.
|
||||
* @see MessageProducerSupport#setOutputChannelName(String)
|
||||
*/
|
||||
public S outputChannel(String outputChannel) {
|
||||
target.setOutputChannelName(outputChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link MessageChannel} to which error messages will be sent.
|
||||
* @param errorChannel the errorChannel.
|
||||
* @return the spec.
|
||||
* @see MessageProducerSupport#setErrorChannel(MessageChannel)
|
||||
*/
|
||||
public S errorChannel(MessageChannel errorChannel) {
|
||||
target.setErrorChannel(errorChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the bean name of the {@link MessageChannel} to which error messages will be sent.
|
||||
* @param errorChannel the errorChannel bean name.
|
||||
* @return the spec.
|
||||
* @see MessageProducerSupport#setErrorChannelName(String)
|
||||
*/
|
||||
public S errorChannel(String errorChannel) {
|
||||
target.setErrorChannelName(errorChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for {@link MessageSource}s.
|
||||
*
|
||||
* @param <S> the target {@link MessageSourceSpec} implementation type.
|
||||
* @param <H> the target {@link MessageSource} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessageSourceSpec<S extends MessageSourceSpec<S, H>, H extends MessageSource<?>>
|
||||
extends IntegrationComponentSpec<S, H> {
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
import org.springframework.integration.mapping.OutboundMessageMapper;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for {@link MessagingGatewaySupport}s.
|
||||
*
|
||||
* @param <S> the target {@link MessagingGatewaySpec} implementation type.
|
||||
* @param <G> the target {@link MessagingGatewaySupport} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessagingGatewaySpec<S extends MessagingGatewaySpec<S, G>, G extends MessagingGatewaySupport>
|
||||
extends IntegrationComponentSpec<S, G> {
|
||||
|
||||
public MessagingGatewaySpec(G gateway) {
|
||||
this.target = gateway;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S id(String id) {
|
||||
this.target.setBeanName(id);
|
||||
return super.id(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A lifecycle phase to use.
|
||||
* @param phase the phase.
|
||||
* @return the spec.
|
||||
* @see org.springframework.context.SmartLifecycle
|
||||
*/
|
||||
public S phase(int phase) {
|
||||
this.target.setPhase(phase);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* An auto-startup flag.
|
||||
* @param autoStartup the autoStartup.
|
||||
* @return the spec.
|
||||
* @see org.springframework.context.SmartLifecycle
|
||||
*/
|
||||
public S autoStartup(boolean autoStartup) {
|
||||
this.target.setAutoStartup(autoStartup);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reply channel to use.
|
||||
* @param replyChannel the replyChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setReplyChannel(MessageChannel)
|
||||
*/
|
||||
public S replyChannel(MessageChannel replyChannel) {
|
||||
this.target.setReplyChannel(replyChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reply channel name to use.
|
||||
* @param replyChannelName the name of replyChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setReplyChannelName(String)
|
||||
*/
|
||||
public S replyChannel(String replyChannelName) {
|
||||
this.target.setReplyChannelName(replyChannelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A request channel to use.
|
||||
* @param requestChannel the requestChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setRequestChannel(MessageChannel)
|
||||
*/
|
||||
public S requestChannel(MessageChannel requestChannel) {
|
||||
this.target.setRequestChannel(requestChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A request channel name to use.
|
||||
* @param requestChannelName the name of requestChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setRequestChannelName(String)
|
||||
*/
|
||||
public S requestChannel(String requestChannelName) {
|
||||
this.target.setRequestChannelName(requestChannelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* An error channel to use.
|
||||
* @param errorChannel the errorChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setErrorChannel(MessageChannel)
|
||||
*/
|
||||
public S errorChannel(MessageChannel errorChannel) {
|
||||
this.target.setErrorChannel(errorChannel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* An error channel name to use.
|
||||
* @param errorChannelName the name of errorChannel.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setErrorChannelName(String)
|
||||
*/
|
||||
public S errorChannel(String errorChannelName) {
|
||||
this.target.setErrorChannelName(errorChannelName);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A request timeout to use.
|
||||
* @param requestTimeout the requestTimeout.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setRequestTimeout(long)
|
||||
*/
|
||||
public S requestTimeout(long requestTimeout) {
|
||||
this.target.setRequestTimeout(requestTimeout);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reply timeout to use.
|
||||
* @param replyTimeout the replyTimeout.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setReplyTimeout(long)
|
||||
*/
|
||||
public S replyTimeout(long replyTimeout) {
|
||||
this.target.setReplyTimeout(replyTimeout);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link InboundMessageMapper} to use.
|
||||
* @param requestMapper the requestMapper.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setRequestMapper(InboundMessageMapper)
|
||||
*/
|
||||
public S requestMapper(InboundMessageMapper<?> requestMapper) {
|
||||
this.target.setRequestMapper(requestMapper);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link OutboundMessageMapper} to use.
|
||||
* @param replyMapper the replyMapper.
|
||||
* @return the spec.
|
||||
* @see MessagingGatewaySupport#setReplyMapper(OutboundMessageMapper)
|
||||
*/
|
||||
public S replyMapper(OutboundMessageMapper<?> replyMapper) {
|
||||
this.target.setReplyMapper(replyMapper);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.scheduling.Trigger;
|
||||
|
||||
/**
|
||||
* An {@code Adapter} class for the {@link Pollers} factory.
|
||||
* Typically used with a Java 8 Lambda expression:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* c -> c.poller(p -> p.fixedRate(100))
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class PollerFactory {
|
||||
|
||||
public PollerSpec trigger(Trigger trigger) {
|
||||
return Pollers.trigger(trigger);
|
||||
}
|
||||
|
||||
public PollerSpec cron(String cronExpression) {
|
||||
return Pollers.cron(cronExpression);
|
||||
}
|
||||
|
||||
public PollerSpec cron(String cronExpression, TimeZone timeZone) {
|
||||
return Pollers.cron(cronExpression, timeZone);
|
||||
}
|
||||
|
||||
public PollerSpec fixedRate(long period) {
|
||||
return Pollers.fixedRate(period);
|
||||
}
|
||||
|
||||
public PollerSpec fixedRate(long period, TimeUnit timeUnit) {
|
||||
return Pollers.fixedRate(period, timeUnit);
|
||||
}
|
||||
|
||||
public PollerSpec fixedRate(long period, long initialDelay) {
|
||||
return Pollers.fixedRate(period, initialDelay);
|
||||
}
|
||||
|
||||
public PollerSpec fixedDelay(long period, TimeUnit timeUnit, long initialDelay) {
|
||||
return Pollers.fixedDelay(period, timeUnit, initialDelay);
|
||||
}
|
||||
|
||||
public PollerSpec fixedRate(long period, TimeUnit timeUnit, long initialDelay) {
|
||||
return Pollers.fixedRate(period, timeUnit, initialDelay);
|
||||
}
|
||||
|
||||
public PollerSpec fixedDelay(long period, TimeUnit timeUnit) {
|
||||
return Pollers.fixedDelay(period, timeUnit);
|
||||
}
|
||||
|
||||
public PollerSpec fixedDelay(long period, long initialDelay) {
|
||||
return Pollers.fixedDelay(period, initialDelay);
|
||||
}
|
||||
|
||||
public PollerSpec fixedDelay(long period) {
|
||||
return Pollers.fixedDelay(period);
|
||||
}
|
||||
|
||||
PollerFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.integration.channel.MessagePublishingErrorHandler;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.transaction.TransactionInterceptorBuilder;
|
||||
import org.springframework.integration.transaction.TransactionSynchronizationFactory;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* An {@link IntegrationComponentSpec} for {@link PollerMetadata}s.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class PollerSpec extends IntegrationComponentSpec<PollerSpec, PollerMetadata>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
private final List<Advice> adviceChain = new LinkedList<>();
|
||||
|
||||
private final Collection<Object> componentsToRegister = new ArrayList<>();
|
||||
|
||||
PollerSpec(Trigger trigger) {
|
||||
this.target = new PollerMetadata();
|
||||
this.target.setAdviceChain(this.adviceChain);
|
||||
this.target.setTrigger(trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link TransactionSynchronizationFactory} to attach a
|
||||
* {@link org.springframework.transaction.support.TransactionSynchronization}
|
||||
* to the transaction around {@code poll} operation.
|
||||
* @param transactionSynchronizationFactory the TransactionSynchronizationFactory to use.
|
||||
* @return the spec.
|
||||
*/
|
||||
public PollerSpec transactionSynchronizationFactory(
|
||||
TransactionSynchronizationFactory transactionSynchronizationFactory) {
|
||||
this.target.setTransactionSynchronizationFactory(transactionSynchronizationFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link ErrorHandler} to wrap a {@code taskExecutor}
|
||||
* to the {@link org.springframework.integration.util.ErrorHandlingTaskExecutor}.
|
||||
* @param errorHandler the {@link ErrorHandler} to use.
|
||||
* @return the spec.
|
||||
* @see #taskExecutor
|
||||
*/
|
||||
public PollerSpec errorHandler(ErrorHandler errorHandler) {
|
||||
this.target.setErrorHandler(errorHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link MessageChannel} to use for sending error message in case
|
||||
* of polling failures.
|
||||
* @param errorChannel the {@link MessageChannel} to use.
|
||||
* @return the spec.
|
||||
* @see MessagePublishingErrorHandler
|
||||
*/
|
||||
public PollerSpec errorChannel(MessageChannel errorChannel) {
|
||||
MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
|
||||
errorHandler.setDefaultErrorChannel(errorChannel);
|
||||
this.componentsToRegister.add(errorHandler);
|
||||
return errorHandler(errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a bean name for the {@link MessageChannel} to use for sending error message in case
|
||||
* of polling failures.
|
||||
* @param errorChannelName the bean name for {@link MessageChannel} to use.
|
||||
* @return the spec.
|
||||
* @see MessagePublishingErrorHandler
|
||||
*/
|
||||
public PollerSpec errorChannel(String errorChannelName) {
|
||||
MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
|
||||
errorHandler.setDefaultErrorChannelName(errorChannelName);
|
||||
this.componentsToRegister.add(errorHandler);
|
||||
return errorHandler(errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxMessagesPerPoll the maxMessagesPerPoll to set.
|
||||
* @return the spec.
|
||||
* @see PollerMetadata#setMaxMessagesPerPoll
|
||||
*/
|
||||
public PollerSpec maxMessagesPerPoll(long maxMessagesPerPoll) {
|
||||
this.target.setMaxMessagesPerPoll(maxMessagesPerPoll);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a timeout in milliseconds to wait for a message in the
|
||||
* {@link org.springframework.messaging.MessageChannel}.
|
||||
* Defaults to {@code 1000}.
|
||||
* @param receiveTimeout the timeout to use.
|
||||
* @return the spec.
|
||||
* @see org.springframework.messaging.PollableChannel#receive(long)
|
||||
*/
|
||||
public PollerSpec receiveTimeout(long receiveTimeout) {
|
||||
this.target.setReceiveTimeout(receiveTimeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify AOP {@link Advice}s for the {@code pollingTask}.
|
||||
* @param advice the {@link Advice}s to use.
|
||||
* @return the spec.
|
||||
*/
|
||||
public PollerSpec advice(Advice... advice) {
|
||||
this.adviceChain.addAll(Arrays.asList(advice));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with the
|
||||
* provided {@code PlatformTransactionManager} and default {@link DefaultTransactionAttribute}
|
||||
* for the {@code pollingTask}.
|
||||
* @param transactionManager the {@link PlatformTransactionManager} to use.
|
||||
* @return the spec.
|
||||
*/
|
||||
public PollerSpec transactional(PlatformTransactionManager transactionManager) {
|
||||
return transactional(new TransactionInterceptorBuilder()
|
||||
.transactionManager(transactionManager)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} with default {@code PlatformTransactionManager}
|
||||
* and {@link DefaultTransactionAttribute} for the {@code pollingTask}.
|
||||
* @return the spec.
|
||||
*/
|
||||
public PollerSpec transactional() {
|
||||
TransactionInterceptor transactionInterceptor = new TransactionInterceptorBuilder().build();
|
||||
this.componentsToRegister.add(transactionInterceptor);
|
||||
return transactional(transactionInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link TransactionInterceptor} {@link Advice} for the {@code pollingTask}.
|
||||
* @param transactionInterceptor the {@link TransactionInterceptor} to use.
|
||||
* @return the spec.
|
||||
* @see TransactionInterceptorBuilder
|
||||
*/
|
||||
public PollerSpec transactional(TransactionInterceptor transactionInterceptor) {
|
||||
return advice(transactionInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an {@link Executor} to perform the {@code pollingTask}.
|
||||
* @param taskExecutor the {@link Executor} to use.
|
||||
* @return the spec.
|
||||
*/
|
||||
public PollerSpec taskExecutor(Executor taskExecutor) {
|
||||
this.target.setTaskExecutor(taskExecutor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PollerSpec sendTimeout(long sendTimeout) {
|
||||
this.target.setSendTimeout(sendTimeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return this.componentsToRegister;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
|
||||
/**
|
||||
* An utility class to provide {@link PollerSpec}s for
|
||||
* {@link org.springframework.integration.scheduling.PollerMetadata} configuration
|
||||
* variants.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class Pollers {
|
||||
|
||||
public static PollerSpec trigger(Trigger trigger) {
|
||||
return new PollerSpec(trigger);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedRate(long period) {
|
||||
return fixedRate(period, null);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedRate(long period, TimeUnit timeUnit) {
|
||||
return fixedRate(period, timeUnit, 0);
|
||||
}
|
||||
public static PollerSpec fixedRate(long period, long initialDelay) {
|
||||
return periodicTrigger(period, null, true, initialDelay);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedRate(long period, TimeUnit timeUnit, long initialDelay) {
|
||||
return periodicTrigger(period, timeUnit, true, initialDelay);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedDelay(long period) {
|
||||
return fixedDelay(period, null);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedDelay(long period, TimeUnit timeUnit) {
|
||||
return fixedDelay(period, timeUnit, 0);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedDelay(long period, long initialDelay) {
|
||||
return periodicTrigger(period, null, false, initialDelay);
|
||||
}
|
||||
|
||||
public static PollerSpec fixedDelay(long period, TimeUnit timeUnit, long initialDelay) {
|
||||
return periodicTrigger(period, timeUnit, false, initialDelay);
|
||||
}
|
||||
|
||||
private static PollerSpec periodicTrigger(long period, TimeUnit timeUnit, boolean fixedRate, long initialDelay) {
|
||||
PeriodicTrigger periodicTrigger = new PeriodicTrigger(period, timeUnit);
|
||||
periodicTrigger.setFixedRate(fixedRate);
|
||||
periodicTrigger.setInitialDelay(initialDelay);
|
||||
return new PollerSpec(periodicTrigger);
|
||||
}
|
||||
|
||||
public static PollerSpec cron(String cronExpression) {
|
||||
return cron(cronExpression, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
public static PollerSpec cron(String cronExpression, TimeZone timeZone) {
|
||||
return new PollerSpec(new CronTrigger(cronExpression, timeZone));
|
||||
}
|
||||
|
||||
private Pollers() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.dsl.channel.PublishSubscribeChannelSpec;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PublishSubscribeSpec extends PublishSubscribeChannelSpec<PublishSubscribeSpec> {
|
||||
|
||||
private final List<Object> subscriberFlows = new ArrayList<>();
|
||||
|
||||
PublishSubscribeSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
PublishSubscribeSpec(Executor executor) {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishSubscribeSpec id(String id) {
|
||||
return super.id(id);
|
||||
}
|
||||
|
||||
public PublishSubscribeSpec subscribe(IntegrationFlow flow) {
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(this.channel);
|
||||
flow.configure(flowBuilder);
|
||||
this.subscriberFlows.add(flowBuilder.get());
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
List<Object> objects = new ArrayList<Object>();
|
||||
objects.addAll(super.getComponentsToRegister());
|
||||
objects.addAll(this.subscriberFlows);
|
||||
return objects;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> the message payload type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
class PublisherIntegrationFlow<T> extends StandardIntegrationFlow implements Publisher<Message<T>> {
|
||||
|
||||
private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() {
|
||||
|
||||
@Override
|
||||
public void request(long n) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final Queue<Subscriber<? super Message<T>>> subscribers = new LinkedBlockingQueue<>();
|
||||
|
||||
private final MessageChannel messageChannel;
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
PublisherIntegrationFlow(Set<Object> integrationComponents, MessageChannel messageChannel, Executor executor) {
|
||||
super(integrationComponents);
|
||||
this.messageChannel = messageChannel;
|
||||
this.executor = executor;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void subscribe(Subscriber<? super Message<T>> subscriber) {
|
||||
if (!isRunning()) {
|
||||
//Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#1.4
|
||||
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
|
||||
subscriber.onError(
|
||||
new IllegalStateException("The Publisher must be started ('Lifecycle.start()') " +
|
||||
"before accepting subscription."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.subscribers.add(subscriber);
|
||||
if (this.messageChannel instanceof SubscribableChannel) {
|
||||
subscriber.onSubscribe(new MessageHandlerSubscription((Subscriber<Message<?>>) subscriber));
|
||||
}
|
||||
else if (this.messageChannel instanceof PollableChannel) {
|
||||
subscriber.onSubscribe(new PollableSubscription((Subscriber<Message<?>>) subscriber));
|
||||
}
|
||||
else {
|
||||
//Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#1.4
|
||||
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
|
||||
subscriber.onError(
|
||||
new IllegalStateException("Unsupported MessageChannel type ["
|
||||
+ this.messageChannel + "]. Must be 'SubscribableChannel' or 'PollableChannel'."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
shutdown();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
Subscriber<? super Message<T>> subscriber;
|
||||
while ((subscriber = this.subscribers.poll()) != null) {
|
||||
subscriber.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private abstract class SubscriberSubscription implements Subscription {
|
||||
|
||||
final Subscriber<Message<?>> subscriber;
|
||||
|
||||
volatile boolean terminated;
|
||||
|
||||
SubscriberSubscription(Subscriber<Message<?>> subscriber) {
|
||||
this.subscriber = subscriber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(long n) {
|
||||
//Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#3.9
|
||||
if (n <= 0L) {
|
||||
this.subscriber.onError(
|
||||
new IllegalArgumentException("Spec. Rule 3.9 - " +
|
||||
"Cannot request a non strictly positive number: " + n));
|
||||
}
|
||||
//Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#3.6
|
||||
else if (!this.terminated && isRunning()) {
|
||||
onRequest(n);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
PublisherIntegrationFlow.this.subscribers.remove(this.subscriber);
|
||||
this.terminated = true;
|
||||
}
|
||||
|
||||
protected abstract void onRequest(long n);
|
||||
|
||||
}
|
||||
|
||||
private final class MessageHandlerSubscription extends SubscriberSubscription implements MessageHandler {
|
||||
|
||||
private final Queue<Long> pendingRequests = new LinkedBlockingQueue<>();
|
||||
|
||||
private final AtomicReference<Long> currentRequest = new AtomicReference<>();
|
||||
|
||||
private final AtomicLong count = new AtomicLong();
|
||||
|
||||
private volatile boolean unbounded;
|
||||
|
||||
MessageHandlerSubscription(Subscriber<Message<?>> subscriber) {
|
||||
super(subscriber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest(long n) {
|
||||
if (n == Long.MAX_VALUE) {
|
||||
this.unbounded = true;
|
||||
this.pendingRequests.clear();
|
||||
this.currentRequest.set(null);
|
||||
this.count.set(0);
|
||||
}
|
||||
else if (!this.unbounded) {
|
||||
if (this.currentRequest.get() != null) {
|
||||
this.pendingRequests.offer(n);
|
||||
}
|
||||
else {
|
||||
this.currentRequest.set(n);
|
||||
this.count.set(0);
|
||||
}
|
||||
}
|
||||
((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).subscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
if (this.terminated || !PublisherIntegrationFlow.this.isRunning()) {
|
||||
((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this);
|
||||
throw new MessageDeliveryException(message);
|
||||
}
|
||||
|
||||
if (this.unbounded) {
|
||||
this.subscriber.onNext(message);
|
||||
}
|
||||
else {
|
||||
if (this.currentRequest.get() == null || this.count.getAndIncrement() == this.currentRequest.get()) {
|
||||
this.currentRequest.set(this.pendingRequests.poll());
|
||||
this.count.set(0);
|
||||
if (this.currentRequest.get() == null) {
|
||||
((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this);
|
||||
throw new MessageDeliveryException(message);
|
||||
}
|
||||
}
|
||||
this.subscriber.onNext(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this);
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private final class PollableSubscription extends SubscriberSubscription {
|
||||
|
||||
PollableSubscription(Subscriber<Message<?>> subscriber) {
|
||||
super(subscriber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest(final long n) {
|
||||
PublisherIntegrationFlow.this.executor.execute(() -> {
|
||||
if (n == Long.MAX_VALUE) {
|
||||
while (!terminated && isRunning()) {
|
||||
Message<?> receive =
|
||||
((PollableChannel) PublisherIntegrationFlow.this.messageChannel).receive(50);
|
||||
if (receive != null) {
|
||||
subscriber.onNext(receive);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
long i = 0;
|
||||
while (!terminated && isRunning() && i < n) {
|
||||
Message<?> receive =
|
||||
((PollableChannel) PublisherIntegrationFlow.this.messageChannel).receive(50);
|
||||
if (receive != null) {
|
||||
subscriber.onNext(receive);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.core.GenericSelector;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.router.RecipientListRouter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AbstractRouterSpec} for a {@link RecipientListRouter}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RecipientListRouterSpec extends AbstractRouterSpec<RecipientListRouterSpec, RecipientListRouter> {
|
||||
|
||||
RecipientListRouterSpec() {
|
||||
super(new DslRecipientListRouter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that always will be selected.
|
||||
* @param channelName the channel name.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(String channelName) {
|
||||
return recipient(channelName, (String) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the expression evaluates to 'true'.
|
||||
* @param channelName the channel name.
|
||||
* @param expression the expression.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(String channelName, String expression) {
|
||||
return recipient(channelName, StringUtils.hasText(expression) ? PARSER.parseExpression(expression) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the expression evaluates to 'true'.
|
||||
* @param channelName the channel name.
|
||||
* @param expression the expression.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(String channelName, Expression expression) {
|
||||
Assert.hasText(channelName);
|
||||
((DslRecipientListRouter) this.target).add(channelName, expression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the selector's accept method returns 'true'.
|
||||
* @param channelName the channel name.
|
||||
* @param selector the selector.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipientMessageSelector(String channelName, MessageSelector selector) {
|
||||
return recipient(channelName, (GenericSelector<Message<?>>) selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the selector's accept method returns 'true'.
|
||||
* @param channelName the channel name.
|
||||
* @param selector the selector.
|
||||
* @param <P> the selector source type.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public <P> RecipientListRouterSpec recipient(String channelName, GenericSelector<P> selector) {
|
||||
Assert.hasText(channelName);
|
||||
((DslRecipientListRouter) this.target).add(channelName, selector);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that always will be selected.
|
||||
* @param channel the recipient channel.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(MessageChannel channel) {
|
||||
return recipient(channel, (String) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the expression evaluates to 'true'.
|
||||
* @param channel the recipient channel.
|
||||
* @param expression the expression.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(MessageChannel channel, String expression) {
|
||||
return recipient(channel, StringUtils.hasText(expression) ? PARSER.parseExpression(expression) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the expression evaluates to 'true'.
|
||||
* @param channel the recipient channel.
|
||||
* @param expression the expression.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipient(MessageChannel channel, Expression expression) {
|
||||
Assert.notNull(channel);
|
||||
((DslRecipientListRouter) this.target).add(channel, expression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the selector's accept method returns 'true'.
|
||||
* @param channel the recipient channel.
|
||||
* @param selector the selector.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipientMessageSelector(MessageChannel channel, MessageSelector selector) {
|
||||
return recipient(channel, (GenericSelector<Message<?>>) selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient channel that will be selected if the the selector's accept method returns 'true'.
|
||||
* @param channel the recipient channel.
|
||||
* @param selector the selector.
|
||||
* @param <P> the selector source type.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public <P> RecipientListRouterSpec recipient(MessageChannel channel, GenericSelector<P> selector) {
|
||||
Assert.notNull(channel);
|
||||
((DslRecipientListRouter) this.target).add(channel, selector);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow that will be invoked if the selector's accept methods returns 'true'.
|
||||
* @param selector the selector.
|
||||
* @param subFlow the subflow.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipientMessageSelectorFlow(MessageSelector selector, IntegrationFlow subFlow) {
|
||||
return recipientFlow((GenericSelector<Message<?>>) selector, subFlow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow that will be invoked if the selector's accept methods returns 'true'.
|
||||
* @param selector the selector.
|
||||
* @param subFlow the subflow.
|
||||
* @param <P> the selector source type.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public <P> RecipientListRouterSpec recipientFlow(GenericSelector<P> selector, IntegrationFlow subFlow) {
|
||||
Assert.notNull(subFlow);
|
||||
DirectChannel channel = populateSubFlow(subFlow);
|
||||
((DslRecipientListRouter) this.target).add(channel, selector);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow that will be invoked as a recipient.
|
||||
* @param subFlow the subflow.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
*/
|
||||
public RecipientListRouterSpec recipientFlow(IntegrationFlow subFlow) {
|
||||
return recipientFlow((String) null, subFlow);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a subflow that will be invoked if the expression evaluates to 'true'.
|
||||
* @param expression the expression.
|
||||
* @param subFlow the subflow.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RecipientListRouterSpec recipientFlow(String expression, IntegrationFlow subFlow) {
|
||||
return recipientFlow(StringUtils.hasText(expression) ? PARSER.parseExpression(expression) : null, subFlow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow that will be invoked if the expression evaluates to 'true'.
|
||||
* @param expression the expression.
|
||||
* @param subFlow the subflow.
|
||||
* @return the router spec.
|
||||
* @since 1.2
|
||||
*/
|
||||
public RecipientListRouterSpec recipientFlow(Expression expression, IntegrationFlow subFlow) {
|
||||
Assert.notNull(subFlow);
|
||||
DirectChannel channel = populateSubFlow(subFlow);
|
||||
((DslRecipientListRouter) this.target).add(channel, expression);
|
||||
return _this();
|
||||
}
|
||||
|
||||
private DirectChannel populateSubFlow(IntegrationFlow subFlow) {
|
||||
DirectChannel channel = new DirectChannel();
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(channel);
|
||||
subFlow.configure(flowBuilder);
|
||||
this.subFlows.add(flowBuilder.get());
|
||||
return channel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.aggregator.ResequencingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.ResequencingMessageHandler;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResequencerSpec extends CorrelationHandlerSpec<ResequencerSpec, ResequencingMessageHandler> {
|
||||
|
||||
ResequencerSpec() {
|
||||
super(new ResequencingMessageHandler(new ResequencingMessageGroupProcessor()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param releasePartialSequences the releasePartialSequences
|
||||
* @return the handler spec.
|
||||
* @see ResequencingMessageHandler#setReleasePartialSequences(boolean)
|
||||
*/
|
||||
public ResequencerSpec releasePartialSequences(boolean releasePartialSequences) {
|
||||
this.handler.setReleasePartialSequences(releasePartialSequences);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.router.AbstractMappingMessageRouter;
|
||||
import org.springframework.integration.support.context.NamedComponent;
|
||||
import org.springframework.integration.support.management.MappingMessageRouterManagement;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The {@link AbstractRouterSpec} for an {@link AbstractMappingMessageRouter}.
|
||||
*
|
||||
* @param <K> the key type.
|
||||
* @param <R> the {@link AbstractMappingMessageRouter} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class RouterSpec<K, R extends AbstractMappingMessageRouter>
|
||||
extends AbstractRouterSpec<RouterSpec<K, R>, R>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
private final RouterMappingProvider mappingProvider;
|
||||
|
||||
private String prefix;
|
||||
|
||||
private String suffix;
|
||||
|
||||
RouterSpec(R router) {
|
||||
super(router);
|
||||
this.mappingProvider = new RouterMappingProvider(this.target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resolutionRequired the resolutionRequired.
|
||||
* @return the router spec.
|
||||
* @see AbstractMappingMessageRouter#setResolutionRequired(boolean)
|
||||
*/
|
||||
public RouterSpec<K, R> resolutionRequired(boolean resolutionRequired) {
|
||||
this.target.setResolutionRequired(resolutionRequired);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be invoked if {@link #subFlowMapping(Object, IntegrationFlow)} is used.
|
||||
* @param prefix the prefix.
|
||||
* @return the router spec.
|
||||
* @see AbstractMappingMessageRouter#setPrefix(String)
|
||||
*/
|
||||
public RouterSpec<K, R> prefix(String prefix) {
|
||||
Assert.state(this.subFlows.isEmpty(), "The 'prefix'('suffix') and 'subFlowMapping' are mutually exclusive");
|
||||
this.prefix = prefix;
|
||||
this.target.setPrefix(prefix);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be invoked if {@link #subFlowMapping(Object, IntegrationFlow)} is used.
|
||||
* @param suffix the suffix to set.
|
||||
* @return the router spec.
|
||||
* @see AbstractMappingMessageRouter#setSuffix(String)
|
||||
*/
|
||||
public RouterSpec<K, R> suffix(String suffix) {
|
||||
Assert.state(this.subFlows.isEmpty(), "The 'prefix'('suffix') and 'subFlowMapping' are mutually exclusive");
|
||||
this.suffix = suffix;
|
||||
this.target.setSuffix(suffix);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key.
|
||||
* @param channelName the channelName.
|
||||
* @return the router spec.
|
||||
* @see AbstractMappingMessageRouter#setChannelMapping(String, String)
|
||||
*/
|
||||
public RouterSpec<K, R> channelMapping(K key, final String channelName) {
|
||||
Assert.notNull(key);
|
||||
Assert.hasText(channelName);
|
||||
if (key instanceof String) {
|
||||
this.target.setChannelMapping((String) key, channelName);
|
||||
}
|
||||
else {
|
||||
this.mappingProvider.addMapping(key, new NamedComponent() {
|
||||
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return channelName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "channel";
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subflow as an alternative to a {@link #channelMapping(Object, String)}.
|
||||
* {@link #prefix(String)} and {@link #suffix(String)} cannot be used when subflow
|
||||
* mappings are used.
|
||||
* @param key the key.
|
||||
* @param subFlow the subFlow.
|
||||
* @return the router spec.
|
||||
*/
|
||||
public RouterSpec<K, R> subFlowMapping(K key, IntegrationFlow subFlow) {
|
||||
Assert.notNull(key);
|
||||
Assert.notNull(subFlow);
|
||||
Assert.state(!(StringUtils.hasText(this.prefix) || StringUtils.hasText(this.suffix)),
|
||||
"The 'prefix'('suffix') and 'subFlowMapping' are mutually exclusive");
|
||||
|
||||
DirectChannel channel = new DirectChannel();
|
||||
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(channel);
|
||||
subFlow.configure(flowBuilder);
|
||||
|
||||
this.subFlows.add(flowBuilder);
|
||||
|
||||
this.mappingProvider.addMapping(key, channel);
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
// The 'mappingProvider' must be added to the 'componentToRegister' in the end to
|
||||
// let all other components to be registered before the 'RouterMappingProvider.onInit()' logic.
|
||||
this.subFlows.add(this.mappingProvider);
|
||||
return super.getComponentsToRegister();
|
||||
}
|
||||
|
||||
private static class RouterMappingProvider extends IntegrationObjectSupport {
|
||||
|
||||
private final MappingMessageRouterManagement router;
|
||||
|
||||
private final Map<Object, NamedComponent> mapping = new HashMap<Object, NamedComponent>();
|
||||
|
||||
RouterMappingProvider(MappingMessageRouterManagement router) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
void addMapping(Object key, NamedComponent channel) {
|
||||
this.mapping.put(key, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
ConversionService conversionService = getConversionService();
|
||||
if (conversionService == null) {
|
||||
conversionService = new DefaultConversionService();
|
||||
}
|
||||
for (Map.Entry<Object, NamedComponent> entry : this.mapping.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
String channelKey;
|
||||
if (key instanceof String) {
|
||||
channelKey = (String) key;
|
||||
}
|
||||
else if (key instanceof Class) {
|
||||
channelKey = ((Class<?>) key).getName();
|
||||
}
|
||||
else if (conversionService.canConvert(key.getClass(), String.class)) {
|
||||
channelKey = conversionService.convert(key, String.class);
|
||||
}
|
||||
else {
|
||||
throw new MessagingException("unsupported channel mapping type for router [" + key.getClass() + "]");
|
||||
}
|
||||
|
||||
this.router.setChannelMapping(channelKey, entry.getValue().getComponentName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.scattergather.ScatterGatherHandler;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* A {@link GenericEndpointSpec} extension for the {@link ScatterGatherHandler}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see ScatterGatherHandler
|
||||
*/
|
||||
public class ScatterGatherSpec extends ConsumerEndpointSpec<ScatterGatherSpec, ScatterGatherHandler> {
|
||||
|
||||
ScatterGatherSpec(ScatterGatherHandler messageHandler) {
|
||||
super(messageHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a {@link MessageChannel} (optional) which is used internally
|
||||
* in the {@link ScatterGatherHandler} for gathering (aggregate) results for scattered requests.
|
||||
* @param gatherChannel the {@link MessageChannel} for gathering results.
|
||||
* @return the current {@link ScatterGatherSpec} instance.
|
||||
*/
|
||||
public ScatterGatherSpec gatherChannel(MessageChannel gatherChannel) {
|
||||
this.handler.setGatherChannel(gatherChannel);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a timeout (in milliseconds) for the
|
||||
* {@link org.springframework.messaging.PollableChannel#receive(long)} operation
|
||||
* to wait for gathering results to output.
|
||||
* Defaults to {@code -1} - to wait indefinitely.
|
||||
* @param gatherTimeout the {@link org.springframework.messaging.PollableChannel} receive timeout.
|
||||
* @return the current {@link ScatterGatherSpec} instance.
|
||||
*/
|
||||
public ScatterGatherSpec gatherTimeout(long gatherTimeout) {
|
||||
this.handler.setGatherTimeout(gatherTimeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class SourcePollingChannelAdapterSpec extends
|
||||
EndpointSpec<SourcePollingChannelAdapterSpec, SourcePollingChannelAdapterFactoryBean, MessageSource<?>> {
|
||||
|
||||
SourcePollingChannelAdapterSpec(MessageSource<?> messageSource) {
|
||||
super(messageSource);
|
||||
this.endpointFactoryBean.setSource(messageSource);
|
||||
}
|
||||
|
||||
public SourcePollingChannelAdapterSpec phase(int phase) {
|
||||
this.endpointFactoryBean.setPhase(phase);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public SourcePollingChannelAdapterSpec autoStartup(boolean autoStartup) {
|
||||
this.endpointFactoryBean.setAutoStartup(autoStartup);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public SourcePollingChannelAdapterSpec poller(PollerMetadata pollerMetadata) {
|
||||
if (pollerMetadata != null) {
|
||||
if (PollerMetadata.MAX_MESSAGES_UNBOUNDED == pollerMetadata.getMaxMessagesPerPoll()) {
|
||||
pollerMetadata.setMaxMessagesPerPoll(1);
|
||||
}
|
||||
this.endpointFactoryBean.setPollerMetadata(pollerMetadata);
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import org.springframework.integration.splitter.AbstractMessageSplitter;
|
||||
import org.springframework.integration.splitter.DefaultMessageSplitter;
|
||||
|
||||
/**
|
||||
* A {@link ConsumerEndpointSpec} for a {@link AbstractMessageSplitter} implementations.
|
||||
*
|
||||
* @param <S> the target {@link SplitterEndpointSpec} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class SplitterEndpointSpec<S extends AbstractMessageSplitter>
|
||||
extends ConsumerEndpointSpec<SplitterEndpointSpec<S>, S> {
|
||||
|
||||
SplitterEndpointSpec(S splitter) {
|
||||
super(splitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the applySequence flag to the specified value. Defaults to {@code true}.
|
||||
* @param applySequence the applySequence.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractMessageSplitter#setApplySequence(boolean)
|
||||
*/
|
||||
public SplitterEndpointSpec<S> applySequence(boolean applySequence) {
|
||||
this.handler.setApplySequence(applySequence);
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set delimiters to tokenize String values. The default is
|
||||
* <code>null</code> indicating that no tokenizing should occur.
|
||||
* If delimiters are provided, they will be applied to any String payload.
|
||||
* Only applied if provided {@code splitter} is instance of {@link DefaultMessageSplitter}.
|
||||
* @param delimiters The delimiters.
|
||||
* @return the endpoint spec.
|
||||
* @see DefaultMessageSplitter#setDelimiters(String)
|
||||
*/
|
||||
public SplitterEndpointSpec<S> delimiters(String delimiters) {
|
||||
if (this.handler instanceof DefaultMessageSplitter) {
|
||||
((DefaultMessageSplitter) this.handler).setDelimiters(delimiters);
|
||||
}
|
||||
else {
|
||||
logger.warn("'delimiters' can be applied only for the DefaultMessageSplitter");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class StandardIntegrationFlow implements IntegrationFlow, SmartLifecycle {
|
||||
|
||||
private final List<Object> integrationComponents;
|
||||
|
||||
private final List<SmartLifecycle> lifecycles = new LinkedList<SmartLifecycle>();
|
||||
|
||||
private final boolean registerComponents = true;
|
||||
|
||||
private boolean running;
|
||||
|
||||
StandardIntegrationFlow(Set<Object> integrationComponents) {
|
||||
this.integrationComponents = new LinkedList<Object>(integrationComponents);
|
||||
}
|
||||
|
||||
//TODO Figure out some custom DestinationResolver when we don't register singletons
|
||||
/*public void setRegisterComponents(boolean registerComponents) {
|
||||
this.registerComponents = registerComponents;
|
||||
}*/
|
||||
|
||||
public boolean isRegisterComponents() {
|
||||
return this.registerComponents;
|
||||
}
|
||||
|
||||
public void setIntegrationComponents(List<Object> integrationComponents) {
|
||||
this.integrationComponents.clear();
|
||||
this.integrationComponents.addAll(integrationComponents);
|
||||
}
|
||||
|
||||
public List<Object> getIntegrationComponents() {
|
||||
return this.integrationComponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(IntegrationFlowDefinition<?> flow) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (!this.running) {
|
||||
ListIterator<Object> iterator = this.integrationComponents.listIterator(this.integrationComponents.size());
|
||||
this.lifecycles.clear();
|
||||
while (iterator.hasPrevious()) {
|
||||
Object component = iterator.previous();
|
||||
if (component instanceof SmartLifecycle) {
|
||||
this.lifecycles.add((SmartLifecycle) component);
|
||||
((SmartLifecycle) component).start();
|
||||
}
|
||||
}
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
if (this.running) {
|
||||
AggregatingCallback aggregatingCallback = new AggregatingCallback(this.lifecycles.size(), callback);
|
||||
ListIterator<SmartLifecycle> iterator = this.lifecycles.listIterator(this.lifecycles.size());
|
||||
while (iterator.hasPrevious()) {
|
||||
SmartLifecycle lifecycle = iterator.previous();
|
||||
if (lifecycle.isRunning()) {
|
||||
lifecycle.stop(aggregatingCallback);
|
||||
}
|
||||
else {
|
||||
aggregatingCallback.run();
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (this.running) {
|
||||
ListIterator<SmartLifecycle> iterator = this.lifecycles.listIterator(this.lifecycles.size());
|
||||
while (iterator.hasPrevious()) {
|
||||
iterator.previous().stop();
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final class AggregatingCallback implements Runnable {
|
||||
|
||||
private final AtomicInteger count;
|
||||
|
||||
private final Runnable finishCallback;
|
||||
|
||||
AggregatingCallback(int count, Runnable finishCallback) {
|
||||
this.count = new AtomicInteger(count);
|
||||
this.finishCallback = finishCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.count.decrementAndGet() <= 0) {
|
||||
this.finishCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.codec.Codec;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.json.JsonToObjectTransformer;
|
||||
import org.springframework.integration.json.ObjectToJsonTransformer;
|
||||
import org.springframework.integration.support.json.JsonObjectMapper;
|
||||
import org.springframework.integration.transformer.DecodingTransformer;
|
||||
import org.springframework.integration.transformer.EncodingPayloadTransformer;
|
||||
import org.springframework.integration.transformer.MapToObjectTransformer;
|
||||
import org.springframework.integration.transformer.ObjectToMapTransformer;
|
||||
import org.springframework.integration.transformer.ObjectToStringTransformer;
|
||||
import org.springframework.integration.transformer.PayloadDeserializingTransformer;
|
||||
import org.springframework.integration.transformer.PayloadSerializingTransformer;
|
||||
import org.springframework.integration.transformer.PayloadTypeConvertingTransformer;
|
||||
import org.springframework.integration.transformer.SyslogToMapTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An utility class to provide methods for out-of-the-box
|
||||
* {@link org.springframework.integration.transformer.Transformer}s.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class Transformers {
|
||||
|
||||
private final static SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
public static ObjectToStringTransformer objectToString() {
|
||||
return objectToString(null);
|
||||
}
|
||||
|
||||
public static ObjectToStringTransformer objectToString(String charset) {
|
||||
return charset != null ? new ObjectToStringTransformer(charset) : new ObjectToStringTransformer();
|
||||
}
|
||||
|
||||
public static ObjectToMapTransformer toMap() {
|
||||
return new ObjectToMapTransformer();
|
||||
}
|
||||
|
||||
public static ObjectToMapTransformer toMap(boolean shouldFlattenKeys) {
|
||||
ObjectToMapTransformer transformer = new ObjectToMapTransformer();
|
||||
transformer.setShouldFlattenKeys(shouldFlattenKeys);
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static MapToObjectTransformer fromMap(Class<?> targetClass) {
|
||||
return new MapToObjectTransformer(targetClass);
|
||||
}
|
||||
|
||||
public static MapToObjectTransformer fromMap(String beanName) {
|
||||
return new MapToObjectTransformer(beanName);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson() {
|
||||
return toJson(null, null, null);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(JsonObjectMapper<?, ?> jsonObjectMapper) {
|
||||
return toJson(jsonObjectMapper, null, null);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(JsonObjectMapper<?, ?> jsonObjectMapper,
|
||||
ObjectToJsonTransformer.ResultType resultType) {
|
||||
return toJson(jsonObjectMapper, resultType, null);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(String contentType) {
|
||||
return toJson(null, null, contentType);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(JsonObjectMapper<?, ?> jsonObjectMapper, String contentType) {
|
||||
return toJson(jsonObjectMapper, null, contentType);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(ObjectToJsonTransformer.ResultType resultType, String contentType) {
|
||||
return toJson(null, resultType, contentType);
|
||||
}
|
||||
|
||||
public static ObjectToJsonTransformer toJson(JsonObjectMapper<?, ?> jsonObjectMapper,
|
||||
ObjectToJsonTransformer.ResultType resultType, String contentType) {
|
||||
ObjectToJsonTransformer transformer;
|
||||
if (jsonObjectMapper != null) {
|
||||
if (resultType != null) {
|
||||
transformer = new ObjectToJsonTransformer(jsonObjectMapper, resultType);
|
||||
}
|
||||
else {
|
||||
transformer = new ObjectToJsonTransformer(jsonObjectMapper);
|
||||
}
|
||||
}
|
||||
else if (resultType != null) {
|
||||
transformer = new ObjectToJsonTransformer(resultType);
|
||||
}
|
||||
else {
|
||||
transformer = new ObjectToJsonTransformer();
|
||||
}
|
||||
if (contentType != null) {
|
||||
transformer.setContentType(contentType);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static JsonToObjectTransformer fromJson() {
|
||||
return fromJson(null, null);
|
||||
}
|
||||
|
||||
public static JsonToObjectTransformer fromJson(Class<?> targetClass) {
|
||||
return fromJson(targetClass, null);
|
||||
}
|
||||
|
||||
public static JsonToObjectTransformer fromJson(JsonObjectMapper<?, ?> jsonObjectMapper) {
|
||||
return fromJson(null, jsonObjectMapper);
|
||||
}
|
||||
|
||||
public static JsonToObjectTransformer fromJson(Class<?> targetClass, JsonObjectMapper<?, ?> jsonObjectMapper) {
|
||||
return new JsonToObjectTransformer(targetClass, jsonObjectMapper);
|
||||
}
|
||||
|
||||
public static PayloadSerializingTransformer serializer() {
|
||||
return serializer(null);
|
||||
}
|
||||
|
||||
public static PayloadSerializingTransformer serializer(Serializer<Object> serializer) {
|
||||
PayloadSerializingTransformer transformer = new PayloadSerializingTransformer();
|
||||
if (serializer != null) {
|
||||
transformer.setSerializer(serializer);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static PayloadDeserializingTransformer deserializer() {
|
||||
return deserializer(null);
|
||||
}
|
||||
|
||||
public static PayloadDeserializingTransformer deserializer(Deserializer<Object> deserializer) {
|
||||
PayloadDeserializingTransformer transformer = new PayloadDeserializingTransformer();
|
||||
if (deserializer != null) {
|
||||
transformer.setDeserializer(deserializer);
|
||||
}
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static <T, U> PayloadTypeConvertingTransformer<T, U> converter(Converter<T, U> converter) {
|
||||
Assert.notNull(converter, "The Converter<?, ?> is required for the PayloadTypeConvertingTransformer");
|
||||
PayloadTypeConvertingTransformer<T, U> transformer = new PayloadTypeConvertingTransformer<>();
|
||||
transformer.setConverter(converter);
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public static SyslogToMapTransformer syslogToMap() {
|
||||
return new SyslogToMapTransformer();
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method for the {@link EncodingPayloadTransformer}.
|
||||
* @param codec the {@link Codec} to use.
|
||||
* @param <T> the {@code payload} type.
|
||||
* @return the {@link EncodingPayloadTransformer} instance.
|
||||
*/
|
||||
public static <T> EncodingPayloadTransformer<T> encoding(Codec codec) {
|
||||
return new EncodingPayloadTransformer<>(codec);
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method for the {@link DecodingTransformer}.
|
||||
* @param codec the {@link Codec} to use.
|
||||
* @param type the target type to transform to.
|
||||
* @param <T> the target type.
|
||||
* @return the {@link DecodingTransformer} instance.
|
||||
*/
|
||||
public static <T> DecodingTransformer<T> decoding(Codec codec, Class<T> type) {
|
||||
return new DecodingTransformer<>(codec, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method for the {@link DecodingTransformer}.
|
||||
* @param codec the {@link Codec} to use.
|
||||
* @param typeExpression the target type SpEL expression.
|
||||
* @param <T> the target type.
|
||||
* @return the {@link DecodingTransformer} instance.
|
||||
*/
|
||||
public static <T> DecodingTransformer<T> decoding(Codec codec, String typeExpression) {
|
||||
return decoding(codec, PARSER.parseExpression(typeExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method for the {@link DecodingTransformer}.
|
||||
* @param codec the {@link Codec} to use.
|
||||
* @param typeFunction the target type function.
|
||||
* @param <T> the target type.
|
||||
* @return the {@link DecodingTransformer} instance.
|
||||
*/
|
||||
public static <T> DecodingTransformer<T> decoding(Codec codec, Function<Message<?>, Class<T>> typeFunction) {
|
||||
return decoding(codec, new FunctionExpression<>(typeFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method for the {@link DecodingTransformer}.
|
||||
* @param codec the {@link Codec} to use.
|
||||
* @param typeExpression the target type SpEL expression.
|
||||
* @param <T> the target type.
|
||||
* @return the {@link DecodingTransformer} instance.
|
||||
*/
|
||||
public static <T> DecodingTransformer<T> decoding(Codec codec, Expression typeExpression) {
|
||||
return new DecodingTransformer<>(codec, typeExpression);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class DirectChannelSpec extends LoadBalancingChannelSpec<DirectChannelSpec, DirectChannel> {
|
||||
|
||||
@Override
|
||||
protected DirectChannel doGet() {
|
||||
this.channel = new DirectChannel(this.loadBalancingStrategy);
|
||||
if (this.failover != null) {
|
||||
this.channel.setFailover(this.failover);
|
||||
}
|
||||
if (this.maxSubscribers != null) {
|
||||
this.channel.setMaxSubscribers(this.maxSubscribers);
|
||||
}
|
||||
return super.doGet();
|
||||
}
|
||||
|
||||
DirectChannelSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.channel.ExecutorChannel;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ExecutorChannelSpec extends LoadBalancingChannelSpec<ExecutorChannelSpec, ExecutorChannel> {
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
ExecutorChannelSpec(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutorChannel doGet() {
|
||||
this.channel = new ExecutorChannel(this.executor, this.loadBalancingStrategy);
|
||||
if (this.failover != null) {
|
||||
this.channel.setFailover(this.failover);
|
||||
}
|
||||
if (this.maxSubscribers != null) {
|
||||
this.channel.setMaxSubscribers(this.maxSubscribers);
|
||||
}
|
||||
return super.doGet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.dispatcher.LoadBalancingStrategy;
|
||||
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <S> the target {@link LoadBalancingChannelSpec} implementation type.
|
||||
* @param <C> the target {@link AbstractMessageChannel} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class LoadBalancingChannelSpec<S extends MessageChannelSpec<S, C>, C extends AbstractMessageChannel>
|
||||
extends MessageChannelSpec<S, C> {
|
||||
|
||||
protected LoadBalancingStrategy loadBalancingStrategy = new RoundRobinLoadBalancingStrategy();
|
||||
|
||||
protected Boolean failover;
|
||||
|
||||
protected Integer maxSubscribers;
|
||||
|
||||
protected LoadBalancingChannelSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
public S loadBalancer(LoadBalancingStrategy loadBalancingStrategy) {
|
||||
this.loadBalancingStrategy = loadBalancingStrategy;
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S failover(Boolean failover) {
|
||||
this.failover = failover;
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S maxSubscribers(Integer maxSubscribers) {
|
||||
this.maxSubscribers = maxSubscribers;
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.channel.interceptor.WireTap;
|
||||
import org.springframework.integration.dsl.ComponentsRegistration;
|
||||
import org.springframework.integration.dsl.IntegrationComponentSpec;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <S> the target {@link MessageChannelSpec} implementation type.
|
||||
* @param <C> the target {@link AbstractMessageChannel} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class MessageChannelSpec<S extends MessageChannelSpec<S, C>, C extends AbstractMessageChannel>
|
||||
extends IntegrationComponentSpec<S, C>
|
||||
implements ComponentsRegistration {
|
||||
|
||||
private final List<Object> componentsToRegister = new ArrayList<>();
|
||||
|
||||
private final List<Class<?>> datatypes = new ArrayList<>();
|
||||
|
||||
private final List<ChannelInterceptor> interceptors = new LinkedList<>();
|
||||
|
||||
protected C channel;
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
protected MessageChannelSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected S id(String id) {
|
||||
return super.id(id);
|
||||
}
|
||||
|
||||
public S datatype(Class<?>... datatypes) {
|
||||
Assert.noNullElements(datatypes);
|
||||
this.datatypes.addAll(Arrays.asList(datatypes));
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S interceptor(ChannelInterceptor... interceptors) {
|
||||
Assert.noNullElements(interceptors);
|
||||
this.interceptors.addAll(Arrays.asList(interceptors));
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@code Wire Tap} EI Pattern specific
|
||||
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation.
|
||||
* @param wireTapChannel the {@link MessageChannel} bean name to wire-tap.
|
||||
* @return the current {@link MessageChannelSpec}.
|
||||
* @see WireTapSpec
|
||||
*/
|
||||
public S wireTap(String wireTapChannel) {
|
||||
return wireTap(new WireTapSpec(wireTapChannel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@code Wire Tap} EI Pattern specific
|
||||
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation.
|
||||
* @param wireTapChannel the {@link MessageChannel} instance to wire-tap.
|
||||
* @return the current {@link MessageChannelSpec}.
|
||||
* @see WireTapSpec
|
||||
*/
|
||||
public S wireTap(MessageChannel wireTapChannel) {
|
||||
return wireTap(new WireTapSpec(wireTapChannel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the {@code Wire Tap} EI Pattern specific
|
||||
* {@link org.springframework.messaging.support.ChannelInterceptor} implementation.
|
||||
* @param wireTapSpec the {@link WireTapSpec} to build {@link WireTap} instance.
|
||||
* @return the current {@link MessageChannelSpec}.
|
||||
* @see WireTap
|
||||
*/
|
||||
public S wireTap(WireTapSpec wireTapSpec) {
|
||||
WireTap interceptor = wireTapSpec.get();
|
||||
this.componentsToRegister.add(interceptor);
|
||||
return interceptor(interceptor);
|
||||
}
|
||||
|
||||
public S messageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
return _this();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
return this.componentsToRegister;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected C doGet() {
|
||||
this.channel.setDatatypes(this.datatypes.toArray(new Class<?>[this.datatypes.size()]));
|
||||
this.channel.setBeanName(getId());
|
||||
this.channel.setInterceptors(this.interceptors);
|
||||
this.channel.setMessageConverter(this.messageConverter);
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.store.ChannelMessageStore;
|
||||
import org.springframework.integration.store.PriorityCapableChannelMessageStore;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class MessageChannels {
|
||||
|
||||
public static DirectChannelSpec direct() {
|
||||
return new DirectChannelSpec();
|
||||
}
|
||||
|
||||
public static DirectChannelSpec direct(String id) {
|
||||
return direct().id(id);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue() {
|
||||
return new QueueChannelSpec();
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue(String id) {
|
||||
return queue().id(id);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue(Queue<Message<?>> queue) {
|
||||
return new QueueChannelSpec(queue);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue(String id, Queue<Message<?>> queue) {
|
||||
return queue(queue).id(id);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue(Integer capacity) {
|
||||
return new QueueChannelSpec(capacity);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec queue(String id, Integer capacity) {
|
||||
return queue(capacity).id(id);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec.MessageStoreSpec queue(ChannelMessageStore messageGroupStore, Object groupId) {
|
||||
return new QueueChannelSpec.MessageStoreSpec(messageGroupStore, groupId);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec.MessageStoreSpec queue(String id, ChannelMessageStore messageGroupStore,
|
||||
Object groupId) {
|
||||
return queue(messageGroupStore, groupId).id(id);
|
||||
}
|
||||
|
||||
public static ExecutorChannelSpec executor(Executor executor) {
|
||||
return new ExecutorChannelSpec(executor);
|
||||
}
|
||||
|
||||
public static ExecutorChannelSpec executor(String id, Executor executor) {
|
||||
return executor(executor).id(id);
|
||||
}
|
||||
|
||||
public static RendezvousChannelSpec rendezvous() {
|
||||
return new RendezvousChannelSpec();
|
||||
}
|
||||
|
||||
public static RendezvousChannelSpec rendezvous(String id) {
|
||||
return rendezvous().id(id);
|
||||
}
|
||||
|
||||
public static PriorityChannelSpec priority() {
|
||||
return new PriorityChannelSpec();
|
||||
}
|
||||
|
||||
public static PriorityChannelSpec priority(String id) {
|
||||
return priority().id(id);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec.MessageStoreSpec priority(PriorityCapableChannelMessageStore messageGroupStore,
|
||||
Object groupId) {
|
||||
return new QueueChannelSpec.MessageStoreSpec(messageGroupStore, groupId);
|
||||
}
|
||||
|
||||
public static QueueChannelSpec.MessageStoreSpec priority(String id,
|
||||
PriorityCapableChannelMessageStore messageGroupStore, Object groupId) {
|
||||
return queue(messageGroupStore, groupId).id(id);
|
||||
}
|
||||
|
||||
public static <S extends PublishSubscribeChannelSpec<S>> PublishSubscribeChannelSpec<S> publishSubscribe() {
|
||||
return new PublishSubscribeChannelSpec<S>();
|
||||
}
|
||||
|
||||
public static <S extends PublishSubscribeChannelSpec<S>> PublishSubscribeChannelSpec<S> publishSubscribe(
|
||||
String id) {
|
||||
return MessageChannels.<S>publishSubscribe().id(id);
|
||||
}
|
||||
|
||||
public static <S extends PublishSubscribeChannelSpec<S>> PublishSubscribeChannelSpec<S> publishSubscribe(
|
||||
Executor executor) {
|
||||
return new PublishSubscribeChannelSpec<S>(executor);
|
||||
}
|
||||
|
||||
public static <S extends PublishSubscribeChannelSpec<S>> PublishSubscribeChannelSpec<S> publishSubscribe(String id,
|
||||
Executor executor) {
|
||||
return MessageChannels.<S>publishSubscribe(executor).id(id);
|
||||
}
|
||||
|
||||
private MessageChannels() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.integration.channel.PriorityChannel;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PriorityChannelSpec extends MessageChannelSpec<PriorityChannelSpec, PriorityChannel> {
|
||||
|
||||
private int capacity;
|
||||
|
||||
private Comparator<Message<?>> comparator;
|
||||
|
||||
public PriorityChannelSpec setCapacity(int capacity) {
|
||||
this.capacity = capacity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PriorityChannelSpec setComparator(Comparator<Message<?>> comparator) {
|
||||
this.comparator = comparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PriorityChannel doGet() {
|
||||
this.channel = new PriorityChannel(this.capacity, this.comparator);
|
||||
return super.doGet();
|
||||
}
|
||||
|
||||
|
||||
PriorityChannelSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <S> the target {@link PublishSubscribeChannelSpec} implementation type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PublishSubscribeChannelSpec<S extends PublishSubscribeChannelSpec<S>>
|
||||
extends MessageChannelSpec<S, PublishSubscribeChannel> {
|
||||
|
||||
protected PublishSubscribeChannelSpec() {
|
||||
this.channel = new PublishSubscribeChannel();
|
||||
}
|
||||
|
||||
protected PublishSubscribeChannelSpec(Executor executor) {
|
||||
this.channel = new PublishSubscribeChannel(executor);
|
||||
}
|
||||
|
||||
public S errorHandler(ErrorHandler errorHandler) {
|
||||
this.channel.setErrorHandler(errorHandler);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S ignoreFailures(boolean ignoreFailures) {
|
||||
this.channel.setIgnoreFailures(ignoreFailures);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S applySequence(boolean applySequence) {
|
||||
this.channel.setApplySequence(applySequence);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S maxSubscribers(Integer maxSubscribers) {
|
||||
this.channel.setMaxSubscribers(maxSubscribers);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S minSubscribers(int minSubscribers) {
|
||||
this.channel.setMinSubscribers(minSubscribers);
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.store.ChannelMessageStore;
|
||||
import org.springframework.integration.store.MessageGroupQueue;
|
||||
import org.springframework.integration.store.PriorityCapableChannelMessageStore;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class QueueChannelSpec extends MessageChannelSpec<QueueChannelSpec, QueueChannel> {
|
||||
|
||||
protected Queue<Message<?>> queue;
|
||||
|
||||
protected Integer capacity;
|
||||
|
||||
QueueChannelSpec() {
|
||||
super();
|
||||
}
|
||||
|
||||
QueueChannelSpec(Queue<Message<?>> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
QueueChannelSpec(Integer capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueueChannel doGet() {
|
||||
if (this.queue != null) {
|
||||
this.channel = new QueueChannel(this.queue);
|
||||
}
|
||||
else if (this.capacity != null) {
|
||||
this.channel = new QueueChannel(this.capacity);
|
||||
}
|
||||
else {
|
||||
this.channel = new QueueChannel();
|
||||
}
|
||||
return super.doGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ChannelMessageStore}-specific {@link QueueChannelSpec} extension.
|
||||
*/
|
||||
public static class MessageStoreSpec extends QueueChannelSpec {
|
||||
|
||||
private final ChannelMessageStore messageGroupStore;
|
||||
|
||||
private final Object groupId;
|
||||
|
||||
private Lock storeLock;
|
||||
|
||||
MessageStoreSpec(ChannelMessageStore messageGroupStore, Object groupId) {
|
||||
super();
|
||||
this.messageGroupStore = messageGroupStore;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageStoreSpec id(String id) {
|
||||
return (MessageStoreSpec) super.id(id);
|
||||
}
|
||||
|
||||
|
||||
public MessageStoreSpec capacity(Integer capacity) {
|
||||
this.capacity = capacity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MessageStoreSpec storeLock(Lock storeLock) {
|
||||
this.storeLock = storeLock;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueueChannel doGet() {
|
||||
if (this.capacity != null) {
|
||||
if (this.storeLock != null) {
|
||||
this.queue = new MessageGroupQueue(this.messageGroupStore, this.groupId, this.capacity,
|
||||
this.storeLock);
|
||||
}
|
||||
else {
|
||||
this.queue = new MessageGroupQueue(this.messageGroupStore, this.groupId, this.capacity);
|
||||
}
|
||||
}
|
||||
else if (this.storeLock != null) {
|
||||
this.queue = new MessageGroupQueue(this.messageGroupStore, this.groupId, this.storeLock);
|
||||
}
|
||||
else {
|
||||
this.queue = new MessageGroupQueue(this.messageGroupStore, this.groupId);
|
||||
}
|
||||
|
||||
((MessageGroupQueue) this.queue).setPriority(
|
||||
this.messageGroupStore instanceof PriorityCapableChannelMessageStore);
|
||||
|
||||
return super.doGet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import org.springframework.integration.channel.RendezvousChannel;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RendezvousChannelSpec extends MessageChannelSpec<RendezvousChannelSpec, RendezvousChannel> {
|
||||
|
||||
RendezvousChannelSpec() {
|
||||
this.channel = new RendezvousChannel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.channel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.channel.interceptor.WireTap;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.dsl.ComponentsRegistration;
|
||||
import org.springframework.integration.dsl.IntegrationComponentSpec;
|
||||
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link IntegrationComponentSpec} implementation for the {@link WireTap} component.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
*/
|
||||
public class WireTapSpec extends IntegrationComponentSpec<WireTapSpec, WireTap> implements ComponentsRegistration {
|
||||
|
||||
private final MessageChannel channel;
|
||||
|
||||
private final String channelName;
|
||||
|
||||
private MessageSelector selector;
|
||||
|
||||
private Long timeout;
|
||||
|
||||
public WireTapSpec(MessageChannel channel) {
|
||||
Assert.notNull(channel, "'channel' must not be null");
|
||||
this.channel = channel;
|
||||
this.channelName = null;
|
||||
}
|
||||
|
||||
public WireTapSpec(String channelName) {
|
||||
Assert.notNull(channelName, "'channelName' must not be null");
|
||||
this.channelName = channelName;
|
||||
this.channel = null;
|
||||
}
|
||||
|
||||
public WireTapSpec selector(String selectorExpression) {
|
||||
return selector(new ExpressionEvaluatingSelector(selectorExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an {@link Expression} for selector.
|
||||
* @param selectorExpression the expression for selector.
|
||||
* @return the current {@link WireTapSpec}
|
||||
* @since 1.2
|
||||
* @see WireTap#WireTap(MessageChannel, MessageSelector)
|
||||
*/
|
||||
public WireTapSpec selector(Expression selectorExpression) {
|
||||
return selector(new ExpressionEvaluatingSelector(selectorExpression));
|
||||
}
|
||||
|
||||
public WireTapSpec selector(MessageSelector selector) {
|
||||
this.selector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WireTapSpec timeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WireTap doGet() {
|
||||
WireTap wireTap;
|
||||
if (this.channel != null) {
|
||||
wireTap = new WireTap(this.channel, this.selector);
|
||||
}
|
||||
else {
|
||||
wireTap = new WireTap(this.channelName, this.selector);
|
||||
}
|
||||
|
||||
if (this.timeout != null) {
|
||||
wireTap.setTimeout(this.timeout);
|
||||
}
|
||||
return wireTap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getComponentsToRegister() {
|
||||
if (this.selector != null) {
|
||||
return Arrays.asList(this.selector, this.target);
|
||||
}
|
||||
else {
|
||||
return Collections.singletonList(this.target);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains MessageChannel Builders DSL.
|
||||
*/
|
||||
package org.springframework.integration.dsl.channel;
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.context;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.support.context.NamedComponent;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A public API for dynamic (manual) registration of {@link IntegrationFlow},
|
||||
* not via standard bean registration phase.
|
||||
* <p>
|
||||
* The bean of this component is provided via framework automatically.
|
||||
* A bean name is based on the decapitalized class name.
|
||||
* It must be injected to the target service before use.
|
||||
* <p>
|
||||
* The typical use-case, and, therefore algorithm, is:
|
||||
* <ul>
|
||||
* <li> create {@link IntegrationFlow} depending of the business logic
|
||||
* <li> register that {@link IntegrationFlow} in this {@link IntegrationFlowContext},
|
||||
* with optional {@code id} and {@code autoStartup} flag
|
||||
* <li> obtain a {@link MessagingTemplate} for that {@link IntegrationFlow}
|
||||
* (if it is started from the {@link MessageChannel}) and send (or send-and-receive)
|
||||
* messages to the {@link IntegrationFlow}
|
||||
* <li> remove the {@link IntegrationFlow} by its {@code id} from this {@link IntegrationFlowContext}
|
||||
* </ul>
|
||||
* <p>
|
||||
* For convenience an associated {@link IntegrationFlowRegistration} is returned after registration.
|
||||
* It can be used for access to the target {@link IntegrationFlow} or for manipulation with its lifecycle.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see IntegrationFlowRegistration
|
||||
*/
|
||||
public final class IntegrationFlowContext implements BeanFactoryAware {
|
||||
|
||||
private final Map<String, IntegrationFlowRegistration> registry = new HashMap<>();
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;
|
||||
|
||||
private IntegrationFlowContext() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
|
||||
"To use Spring Integration Java DSL the 'beanFactory' has to be an instance of " +
|
||||
"'ConfigurableListableBeanFactory'. " +
|
||||
"Consider using 'GenericApplicationContext' implementation.");
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
|
||||
this.autowiredAnnotationBeanPostProcessor.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate provided {@link IntegrationFlow} with an {@link IntegrationFlowRegistrationBuilder}
|
||||
* for additional options and farther registration in the application context.
|
||||
* @param integrationFlow the {@link IntegrationFlow} to register
|
||||
* @return the IntegrationFlowRegistrationBuilder associated with the provided {@link IntegrationFlow}
|
||||
*/
|
||||
public IntegrationFlowRegistrationBuilder registration(IntegrationFlow integrationFlow) {
|
||||
return new IntegrationFlowRegistrationBuilder(integrationFlow);
|
||||
}
|
||||
|
||||
private void register(IntegrationFlowRegistrationBuilder builder) {
|
||||
IntegrationFlow integrationFlow = builder.integrationFlowRegistration.getIntegrationFlow();
|
||||
String flowId = builder.integrationFlowRegistration.getId();
|
||||
if (flowId == null) {
|
||||
flowId = generateBeanName(integrationFlow, null);
|
||||
builder.id(flowId);
|
||||
}
|
||||
IntegrationFlow theFlow = (IntegrationFlow) registerBean(integrationFlow, flowId, null);
|
||||
builder.integrationFlowRegistration.setIntegrationFlow(theFlow);
|
||||
|
||||
final String theFlowId = flowId;
|
||||
builder.additionalBeans.forEach((key, value) -> registerBean(key, value, theFlowId));
|
||||
|
||||
if (builder.autoStartup) {
|
||||
builder.integrationFlowRegistration.start();
|
||||
}
|
||||
this.registry.put(flowId, builder.integrationFlowRegistration);
|
||||
}
|
||||
|
||||
private Object registerBean(Object bean, String beanName, String parentName) {
|
||||
if (beanName == null) {
|
||||
beanName = generateBeanName(bean, parentName);
|
||||
}
|
||||
|
||||
this.autowiredAnnotationBeanPostProcessor.processInjection(bean);
|
||||
bean = this.beanFactory.initializeBean(bean, beanName);
|
||||
this.beanFactory.registerSingleton(beanName, bean);
|
||||
if (parentName != null) {
|
||||
this.beanFactory.registerDependentBean(parentName, beanName);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an {@link IntegrationFlowRegistration} for the {@link IntegrationFlow}
|
||||
* associated with the provided {@code flowId}.
|
||||
* @param flowId the bean name to obtain
|
||||
* @return the IntegrationFlowRegistration for provided {@code id} or {@code null}
|
||||
*/
|
||||
public IntegrationFlowRegistration getRegistrationById(String flowId) {
|
||||
return this.registry.get(flowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an {@link IntegrationFlow} bean (as well as all its dependant beans)
|
||||
* for provided {@code flowId} and clean up all the local cache for it.
|
||||
* @param flowId the bean name to destroy from
|
||||
*/
|
||||
public synchronized void remove(String flowId) {
|
||||
if (this.registry.containsKey(flowId)) {
|
||||
IntegrationFlowRegistration flowRegistration = this.registry.remove(flowId);
|
||||
flowRegistration.stop();
|
||||
((DefaultSingletonBeanRegistry) this.beanFactory).destroySingleton(flowId);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Only manually registered IntegrationFlows can be removed. "
|
||||
+ "But [" + flowId + "] ins't one of them.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link MessagingTemplate} with its default destination set to the input channel
|
||||
* of the {@link IntegrationFlow} for provided {@code flowId}.
|
||||
* <p> Any {@link IntegrationFlow} bean (not only manually registered) can be used for this method.
|
||||
* <p> If {@link IntegrationFlow} doesn't start with the {@link MessageChannel}, the
|
||||
* {@link IllegalStateException} is thrown.
|
||||
* @param flowId the bean name to obtain the input channel from
|
||||
* @return the {@link MessagingTemplate} instance
|
||||
*/
|
||||
public MessagingTemplate messagingTemplateFor(String flowId) {
|
||||
return this.registry.get(flowId)
|
||||
.getMessagingTemplate();
|
||||
}
|
||||
|
||||
private String generateBeanName(Object instance, String parentName) {
|
||||
if (instance instanceof NamedComponent && ((NamedComponent) instance).getComponentName() != null) {
|
||||
return ((NamedComponent) instance).getComponentName();
|
||||
}
|
||||
String generatedBeanName = (parentName != null ? parentName : "") + instance.getClass().getName();
|
||||
String id = generatedBeanName;
|
||||
int counter = -1;
|
||||
while (counter == -1 || this.beanFactory.containsBean(id)) {
|
||||
counter++;
|
||||
id = generatedBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Builder pattern implementation for the options to register {@link IntegrationFlow}
|
||||
* in the application context.
|
||||
*/
|
||||
public final class IntegrationFlowRegistrationBuilder {
|
||||
|
||||
private Map<Object, String> additionalBeans = new HashMap<Object, String>();
|
||||
|
||||
private final IntegrationFlowRegistration integrationFlowRegistration;
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
IntegrationFlowRegistrationBuilder(IntegrationFlow integrationFlow) {
|
||||
this.integrationFlowRegistration = new IntegrationFlowRegistration(integrationFlow);
|
||||
this.integrationFlowRegistration.setBeanFactory(IntegrationFlowContext.this.beanFactory);
|
||||
this.integrationFlowRegistration.setIntegrationFlowContext(IntegrationFlowContext.this);
|
||||
}
|
||||
|
||||
public IntegrationFlowRegistrationBuilder id(String id) {
|
||||
this.integrationFlowRegistration.setId(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntegrationFlowRegistrationBuilder autoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntegrationFlowRegistrationBuilder addBean(Object bean) {
|
||||
return addBean(null, bean);
|
||||
}
|
||||
|
||||
public IntegrationFlowRegistrationBuilder addBean(String name, Object bean) {
|
||||
this.additionalBeans.put(bean, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntegrationFlowRegistration register() {
|
||||
IntegrationFlowContext.this.register(this);
|
||||
return this.integrationFlowRegistration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.context;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.StandardIntegrationFlow;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* Instances of this classes are returned as a result of
|
||||
* {@link IntegrationFlowContext#registration(IntegrationFlow)} invocation
|
||||
* and provide an API for some useful {@link IntegrationFlow} options and its lifecycle.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*
|
||||
* @see IntegrationFlowContext
|
||||
*/
|
||||
public class IntegrationFlowRegistration {
|
||||
|
||||
private IntegrationFlow integrationFlow;
|
||||
|
||||
private IntegrationFlowContext integrationFlowContext;
|
||||
|
||||
private String id;
|
||||
|
||||
private MessageChannel inputChannel;
|
||||
|
||||
private MessagingTemplate messagingTemplate;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
IntegrationFlowRegistration(IntegrationFlow integrationFlow) {
|
||||
this.integrationFlow = integrationFlow;
|
||||
}
|
||||
|
||||
void setBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
void setIntegrationFlowContext(IntegrationFlowContext integrationFlowContext) {
|
||||
this.integrationFlowContext = integrationFlowContext;
|
||||
}
|
||||
|
||||
void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
void setIntegrationFlow(IntegrationFlow integrationFlow) {
|
||||
this.integrationFlow = integrationFlow;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public IntegrationFlow getIntegrationFlow() {
|
||||
return this.integrationFlow;
|
||||
}
|
||||
|
||||
public MessageChannel getInputChannel() {
|
||||
if (this.inputChannel == null) {
|
||||
synchronized (this) {
|
||||
if (this.inputChannel == null) {
|
||||
if (this.integrationFlow instanceof StandardIntegrationFlow) {
|
||||
StandardIntegrationFlow integrationFlow = (StandardIntegrationFlow) this.integrationFlow;
|
||||
Object next = integrationFlow.getIntegrationComponents().iterator().next();
|
||||
if (next instanceof MessageChannel) {
|
||||
this.inputChannel = (MessageChannel) next;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("The 'IntegrationFlow' [" + integrationFlow + "] " +
|
||||
"doesn't start with 'MessageChannel' for direct message sending.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Only 'StandardIntegrationFlow' instances " +
|
||||
"(e.g. extracted from 'IntegrationFlow' Lambdas) can be used " +
|
||||
"for direct 'send' operation. " +
|
||||
"But [" + this.integrationFlow + "] ins't one of them.\n" +
|
||||
"Consider 'BeanFactory.getBean()' usage for sending messages " +
|
||||
"to the required 'MessageChannel'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.inputChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link MessagingTemplate} with its default destination set to the input channel
|
||||
* of the {@link IntegrationFlow}.
|
||||
* <p> Any {@link IntegrationFlow} bean (not only manually registered) can be used for this method.
|
||||
* <p> If {@link IntegrationFlow} doesn't start with the {@link MessageChannel}, the
|
||||
* {@link IllegalStateException} is thrown.
|
||||
* @return the {@link MessagingTemplate} instance
|
||||
*/
|
||||
public MessagingTemplate getMessagingTemplate() {
|
||||
if (this.messagingTemplate == null) {
|
||||
synchronized (this) {
|
||||
if (this.messagingTemplate == null) {
|
||||
this.messagingTemplate = new MessagingTemplate(getInputChannel()) {
|
||||
|
||||
@Override
|
||||
public Message<?> receive() {
|
||||
return receiveAndConvert(Message.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T receiveAndConvert(Class<T> targetClass) {
|
||||
throw new UnsupportedOperationException("The 'receive()/receiveAndConvert()' " +
|
||||
"isn't supported on the 'IntegrationFlow' input channel.");
|
||||
}
|
||||
|
||||
};
|
||||
this.messagingTemplate.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.messagingTemplate;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.integrationFlow instanceof Lifecycle) {
|
||||
((Lifecycle) this.integrationFlow).start();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("For 'autoStartup' mode the 'IntegrationFlow' " +
|
||||
"must be an instance of 'Lifecycle'.\n" +
|
||||
"Consider to implement it for [" + this.integrationFlow + "]. " +
|
||||
"Or start dependent components on their own.");
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.integrationFlow instanceof Lifecycle) {
|
||||
((Lifecycle) this.integrationFlow).stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the {@link IntegrationFlow} bean (as well as all its dependant beans)
|
||||
* and clean up all the local cache for it.
|
||||
*/
|
||||
public void destroy() {
|
||||
this.integrationFlowContext.remove(this.id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* The context support classes for Spring Integration Java DSL.
|
||||
*/
|
||||
package org.springframework.integration.dsl.context;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Root package of the Spring Integration Java DSL.
|
||||
*/
|
||||
package org.springframework.integration.dsl;
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.support;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* An "artificial" {@link MessageChannel} implementation which will be unwrapped to the
|
||||
* {@link org.springframework.integration.channel.FixedSubscriberChannel} on the bean
|
||||
* registration phase.
|
||||
* For internal use only.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*
|
||||
* @see org.springframework.integration.config.dsl.IntegrationFlowBeanPostProcessor
|
||||
*/
|
||||
public class FixedSubscriberChannelPrototype implements MessageChannel {
|
||||
|
||||
private final String name;
|
||||
|
||||
public FixedSubscriberChannelPrototype() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public FixedSubscriberChannelPrototype(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean send(Message<?> message) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean send(Message<?> message, long timeout) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FixedSubscriberChannelPrototype{" +
|
||||
"name='" + this.name + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.support;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An "artificial" {@link MessageChannel} implementation which will be unwrapped to the
|
||||
* {@link MessageChannel} bean on the bean registration phase.
|
||||
* For internal use only.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see org.springframework.integration.config.dsl.IntegrationFlowBeanPostProcessor
|
||||
*/
|
||||
public class MessageChannelReference implements MessageChannel {
|
||||
|
||||
private final String name;
|
||||
|
||||
public MessageChannelReference(String name) {
|
||||
Assert.notNull(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean send(Message<?> message) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean send(Message<?> message, long timeout) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides various support classes used across Spring Integration Java DSL Components.
|
||||
*/
|
||||
package org.springframework.integration.dsl.support;
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2016 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.endpoint;
|
||||
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
|
||||
/**
|
||||
* The {@link org.springframework.integration.core.MessageSource} strategy implementation
|
||||
* to produce a {@link org.springframework.messaging.Message} from underlying
|
||||
* {@linkplain #messageProcessor} for polling endpoints.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class MessageProcessorMessageSource extends AbstractMessageSource<Object> {
|
||||
|
||||
private final MessageProcessor<?> messageProcessor;
|
||||
|
||||
public MessageProcessorMessageSource(MessageProcessor<?> messageProcessor) {
|
||||
this.messageProcessor = messageProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "inbound-channel-adapter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doReceive() {
|
||||
return this.messageProcessor.processMessage(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2016 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.handler;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An "artificial" {@link MessageProcessor} for lazy-load of target bean by its name.
|
||||
* For internal use only.
|
||||
*
|
||||
* @param <T> the expected {@link #processMessage} result type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BeanNameMessageProcessor<T> implements MessageProcessor<T>, BeanFactoryAware {
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private MessageProcessor<T> delegate;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public BeanNameMessageProcessor(String object, String methodName) {
|
||||
this.beanName = object;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.notNull(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T processMessage(Message<?> message) {
|
||||
if (this.delegate == null) {
|
||||
Object target = this.beanFactory.getBean(this.beanName);
|
||||
this.delegate = new MethodInvokingMessageProcessor<>(target, this.methodName);
|
||||
}
|
||||
return this.delegate.processMessage(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2016 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.handler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A functional interface to specify {@link org.springframework.messaging.MessageHandler}
|
||||
* logic with Java 8 Lambda expression:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* .<Integer>handle((p, h) -> p / 2)
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param <P> the expected {@code payload} type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GenericHandler<P> {
|
||||
|
||||
Object handle(P payload, Map<String, Object> headers);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2016 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.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* A {@code Builder} pattern implementation for the {@link Map}.
|
||||
*
|
||||
* @param <B> The type of target {@link MapBuilder} implementation.
|
||||
* @param <K> The Map key type.
|
||||
* @param <V> The Map value type.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 5.0
|
||||
*/
|
||||
public class MapBuilder<B extends MapBuilder<B, K, V>, K, V> {
|
||||
|
||||
protected final static SpelExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private final Map<K, V> map = new HashMap<K, V>();
|
||||
|
||||
public B put(K key, V value) {
|
||||
this.map.put(key, value);
|
||||
return _this();
|
||||
}
|
||||
|
||||
public Map<K, V> get() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final B _this() {
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2016 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.support;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
/**
|
||||
* A {@code Builder} pattern implementation for the {@link Properties}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class PropertiesBuilder {
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
public PropertiesBuilder put(Object key, Object value) {
|
||||
this.properties.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Properties get() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2016 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.support;
|
||||
|
||||
/**
|
||||
* A map builder creating a map with String keys and values.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class StringStringMapBuilder extends MapBuilder<StringStringMapBuilder, String, String> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 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.transaction;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.integration.handler.advice.HandleMessageAdvice;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.interceptor.TransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
|
||||
/**
|
||||
* A {@link TransactionInterceptor} extension with {@link HandleMessageAdvice} marker.
|
||||
* <p>
|
||||
* When this {@link Advice} is used from the {@code request-handler-advice-chain}, it is applied
|
||||
* to the {@link MessageHandler#handleMessage}
|
||||
* (not to the
|
||||
* {@link org.springframework.integration.handler.AbstractReplyProducingMessageHandler.RequestHandler#handleRequestMessage}),
|
||||
* therefore the entire downstream process is wrapped to the transaction.
|
||||
* <p>
|
||||
* In any other cases it is operated as a regular {@link TransactionInterceptor}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TransactionHandleMessageAdvice extends TransactionInterceptor implements HandleMessageAdvice {
|
||||
|
||||
public TransactionHandleMessageAdvice() {
|
||||
}
|
||||
|
||||
public TransactionHandleMessageAdvice(PlatformTransactionManager ptm, Properties attributes) {
|
||||
super(ptm, attributes);
|
||||
}
|
||||
|
||||
public TransactionHandleMessageAdvice(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
|
||||
super(ptm, tas);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2016 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.transaction;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides a fluent API to build a transaction interceptor. See
|
||||
* {@link TransactionAttribute} for property meanings; if a {@link TransactionAttribute}
|
||||
* is provided, the individual properties are ignored. If a
|
||||
* {@link PlatformTransactionManager} is not provided, a single instance of
|
||||
* {@link PlatformTransactionManager} will be discovered at runtime; if you have more
|
||||
* than one transaction manager, you must inject the one you want to use here.
|
||||
* <p>
|
||||
* When the {@code handleMessageAdvice} option is in use, this builder produces
|
||||
* {@link TransactionHandleMessageAdvice} instance.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
*/
|
||||
public class TransactionInterceptorBuilder {
|
||||
|
||||
private final DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute();
|
||||
|
||||
private final TransactionInterceptor transactionInterceptor;
|
||||
|
||||
public TransactionInterceptorBuilder() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder(boolean handleMessageAdvice) {
|
||||
if (handleMessageAdvice) {
|
||||
this.transactionInterceptor = new TransactionHandleMessageAdvice();
|
||||
}
|
||||
else {
|
||||
this.transactionInterceptor = new TransactionInterceptor();
|
||||
}
|
||||
transactionAttribute(this.transactionAttribute);
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder propagation(Propagation propagation) {
|
||||
Assert.notNull(propagation, "'propagation' must not be null.");
|
||||
this.transactionAttribute.setPropagationBehavior(propagation.value());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder isolation(Isolation isolation) {
|
||||
Assert.notNull(isolation, "'isolation' must not be null.");
|
||||
this.transactionAttribute.setIsolationLevel(isolation.value());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder timeout(int timeout) {
|
||||
this.transactionAttribute.setTimeout(timeout);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder readOnly(boolean readOnly) {
|
||||
this.transactionAttribute.setReadOnly(readOnly);
|
||||
return this;
|
||||
}
|
||||
|
||||
public final TransactionInterceptorBuilder transactionAttribute(TransactionAttribute transactionAttribute) {
|
||||
MatchAlwaysTransactionAttributeSource txAttributeSource = new MatchAlwaysTransactionAttributeSource();
|
||||
txAttributeSource.setTransactionAttribute(transactionAttribute);
|
||||
this.transactionInterceptor.setTransactionAttributeSource(txAttributeSource);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionInterceptorBuilder transactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionInterceptor.setTransactionManager(transactionManager);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionInterceptor build() {
|
||||
return this.transactionInterceptor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
org.springframework.integration.config.IntegrationConfigurationInitializer=\
|
||||
org.springframework.integration.config.GlobalChannelInterceptorInitializer,\
|
||||
org.springframework.integration.config.IntegrationConverterInitializer,\
|
||||
org.springframework.integration.config.IdempotentReceiverAutoProxyCreatorInitializer
|
||||
org.springframework.integration.config.IdempotentReceiverAutoProxyCreatorInitializer,\
|
||||
org.springframework.integration.config.dsl.DslIntegrationConfigurationInitializer
|
||||
|
||||
@@ -192,7 +192,7 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
.build();
|
||||
handler.handleMessage(message);
|
||||
|
||||
handler.setMinimumTimeoutForEmptyGroups(100);
|
||||
handler.setMinimumTimeoutForEmptyGroups(10_000);
|
||||
|
||||
assertEquals(1, outputMessages.size());
|
||||
|
||||
@@ -200,6 +200,8 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
groupStore.expireMessageGroups(0);
|
||||
assertEquals(1, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
|
||||
|
||||
handler.setMinimumTimeoutForEmptyGroups(10);
|
||||
|
||||
int n = 0;
|
||||
|
||||
while (n++ < 200) {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class LambdaMessageProcessorTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("divzero")
|
||||
public void testException() {
|
||||
try {
|
||||
handle((m, h) -> 1 / 0);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e.getCause(), instanceOf(ArithmeticException.class));
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(GenericHandler<?> h) {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(h, String.class);
|
||||
lmp.setBeanFactory(mock(BeanFactory.class));
|
||||
lmp.processMessage(new GenericMessage<>("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.correlation;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.handler.MessageTriggerAction;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class CorrelationHandlerTests {
|
||||
|
||||
private static final String BARRIER = "barrier";
|
||||
|
||||
@Autowired
|
||||
@Qualifier("splitResequenceFlow.input")
|
||||
private MessageChannel splitInput;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("splitAggregateInput")
|
||||
private MessageChannel splitAggregateInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("publishSubscribeFlow.input")
|
||||
private MessageChannel subscriberAggregateFlowInput;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel subscriberAggregateResult;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("barrierFlow.input")
|
||||
private MessageChannel barrierFlowInput;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel barrierResults;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel releaseChannel;
|
||||
|
||||
@Test
|
||||
public void testSplitterResequencer() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
|
||||
this.splitInput.send(MessageBuilder.withPayload("")
|
||||
.setReplyChannel(replyChannel)
|
||||
.setHeader("foo", "bar")
|
||||
.build());
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Message<?> receive = replyChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertFalse(receive.getHeaders().containsKey("foo"));
|
||||
assertTrue(receive.getHeaders().containsKey("FOO"));
|
||||
assertEquals("BAR", receive.getHeaders().get("FOO"));
|
||||
assertEquals(i + 1, receive.getPayload());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitterAggregator() {
|
||||
List<Character> payload = Arrays.asList('a', 'b', 'c', 'd', 'e');
|
||||
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
this.splitAggregateInput.send(MessageBuilder.withPayload(payload)
|
||||
.setReplyChannel(replyChannel)
|
||||
.build());
|
||||
|
||||
Message<?> receive = replyChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive.getPayload(), instanceOf(List.class));
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> result = (List<Object>) receive.getPayload();
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
assertEquals(payload.get(i), result.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubscriberAggregateFlow() {
|
||||
this.subscriberAggregateFlowInput.send(new GenericMessage<>("test"));
|
||||
|
||||
Message<?> receive1 = this.subscriberAggregateResult.receive(10000);
|
||||
assertNotNull(receive1);
|
||||
assertEquals("Hello World!", receive1.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBarrier() {
|
||||
Message<?> releasing = MessageBuilder.withPayload("bar").setHeader(BARRIER, "foo").build();
|
||||
this.releaseChannel.send(releasing);
|
||||
Message<?> suspending = MessageBuilder.withPayload("foo").setHeader(BARRIER, "foo").build();
|
||||
this.barrierFlowInput.send(suspending);
|
||||
Message<?> out = this.barrierResults.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("bar", out.getPayload());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public Executor taskExecutor() {
|
||||
return Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestSplitterPojo testSplitterData() {
|
||||
List<String> first = new ArrayList<>();
|
||||
first.add("1,2,3");
|
||||
first.add("4,5,6");
|
||||
|
||||
List<String> second = new ArrayList<>();
|
||||
second.add("7,8,9");
|
||||
second.add("10,11,12");
|
||||
|
||||
return new TestSplitterPojo(first, second);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow splitResequenceFlow() {
|
||||
return f -> f.enrichHeaders(s -> s.header("FOO", "BAR"))
|
||||
.split("testSplitterData", "buildList", c -> c.applySequence(false))
|
||||
.channel(MessageChannels.executor(taskExecutor()))
|
||||
.split(Message.class, m -> m.getPayload(), c -> c.applySequence(false))
|
||||
.channel(MessageChannels.executor(taskExecutor()))
|
||||
.split(s -> s
|
||||
.applySequence(false)
|
||||
.delimiters(","))
|
||||
.channel(MessageChannels.executor(taskExecutor()))
|
||||
.<String, Integer>transform(Integer::parseInt)
|
||||
.enrichHeaders(h ->
|
||||
h.headerFunction(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, Message::getPayload))
|
||||
.resequence(r -> r.releasePartialSequences(true).correlationExpression("'foo'"))
|
||||
.headerFilter("foo", false);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow splitAggregateFlow() {
|
||||
return IntegrationFlows.from("splitAggregateInput", true)
|
||||
.split()
|
||||
.channel(MessageChannels.executor(taskExecutor()))
|
||||
.resequence()
|
||||
.aggregate()
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow publishSubscribeFlow() {
|
||||
return flow -> flow
|
||||
.publishSubscribeChannel(s -> s
|
||||
.applySequence(true)
|
||||
.subscribe(f -> f
|
||||
.handle((p, h) -> "Hello")
|
||||
.channel("publishSubscribeAggregateFlow.input"))
|
||||
.subscribe(f -> f
|
||||
.handle((p, h) -> "World!")
|
||||
.channel("publishSubscribeAggregateFlow.input"))
|
||||
);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow publishSubscribeAggregateFlow() {
|
||||
return flow -> flow
|
||||
.aggregate(a -> a.outputProcessor(g -> g.getMessages()
|
||||
.stream()
|
||||
.map(m -> (String) m.getPayload())
|
||||
.collect(Collectors.joining(" "))))
|
||||
.channel(MessageChannels.queue("subscriberAggregateResult"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow barrierFlow() {
|
||||
return f -> f
|
||||
.barrier(10000, b -> b
|
||||
.correlationStrategy(new HeaderAttributeCorrelationStrategy(BARRIER))
|
||||
.outputProcessor(g ->
|
||||
g.getMessages()
|
||||
.stream()
|
||||
.skip(1)
|
||||
.findFirst()
|
||||
.get()))
|
||||
.channel(MessageChannels.queue("barrierResults"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@DependsOn("barrierFlow")
|
||||
public IntegrationFlow releaseBarrierFlow(MessageTriggerAction barrierTriggerAction) {
|
||||
return IntegrationFlows.from(MessageChannels.queue("releaseChannel"))
|
||||
.trigger(barrierTriggerAction,
|
||||
e -> e.poller(p -> p.fixedDelay(100)))
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TestSplitterPojo {
|
||||
|
||||
final List<String> first;
|
||||
|
||||
final List<String> second;
|
||||
|
||||
TestSplitterPojo(List<String> first, List<String> second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public List<String> getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public List<String> getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public List<List<String>> buildList() {
|
||||
return Arrays.asList(this.first, this.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.flows;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.MessageDispatchingException;
|
||||
import org.springframework.integration.MessageRejectedException;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.FixedSubscriberChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.Pollers;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.MutableMessageBuilder;
|
||||
import org.springframework.integration.transformer.PayloadDeserializingTransformer;
|
||||
import org.springframework.integration.transformer.PayloadSerializingTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Tim Ysewyn
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class IntegrationFlowTests {
|
||||
|
||||
@Autowired
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
private ControlBusGateway controlBus;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inputChannel")
|
||||
private MessageChannel inputChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("discardChannel")
|
||||
private PollableChannel discardChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("foo")
|
||||
private SubscribableChannel foo;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("successChannel")
|
||||
private PollableChannel successChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlowInput")
|
||||
private PollableChannel bridgeFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlowOutput")
|
||||
private PollableChannel bridgeFlowOutput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlow2Input")
|
||||
private MessageChannel bridgeFlow2Input;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlow2Output")
|
||||
private PollableChannel bridgeFlow2Output;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("methodInvokingInput")
|
||||
private MessageChannel methodInvokingInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("delayedAdvice")
|
||||
private DelayedAdvice delayedAdvice;
|
||||
|
||||
@Autowired
|
||||
private MessageStore messageStore;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("claimCheckInput")
|
||||
private MessageChannel claimCheckInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("lambdasInput")
|
||||
private MessageChannel lambdasInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("gatewayInput")
|
||||
private MessageChannel gatewayInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("gatewayError")
|
||||
private PollableChannel gatewayError;
|
||||
|
||||
@Test
|
||||
public void testDirectFlow() {
|
||||
assertTrue(this.beanFactory.containsBean("filter"));
|
||||
assertTrue(this.beanFactory.containsBean("filter.handler"));
|
||||
assertTrue(this.beanFactory.containsBean("expressionFilter"));
|
||||
assertTrue(this.beanFactory.containsBean("expressionFilter.handler"));
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<String> message = MessageBuilder.withPayload("100").setReplyChannel(replyChannel).build();
|
||||
try {
|
||||
this.inputChannel.send(message);
|
||||
fail("Expected MessageDispatchingException");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(MessageDeliveryException.class));
|
||||
assertThat(e.getCause(), instanceOf(MessageDispatchingException.class));
|
||||
assertThat(e.getMessage(), containsString("Dispatcher has no subscribers"));
|
||||
}
|
||||
this.controlBus.send("@payloadSerializingTransformer.start()");
|
||||
|
||||
final AtomicBoolean used = new AtomicBoolean();
|
||||
|
||||
this.foo.subscribe(m -> used.set(true));
|
||||
|
||||
this.inputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(5000);
|
||||
assertNotNull(reply);
|
||||
assertEquals(200, reply.getPayload());
|
||||
|
||||
Message<?> successMessage = this.successChannel.receive(5000);
|
||||
assertNotNull(successMessage);
|
||||
assertEquals(100, successMessage.getPayload());
|
||||
|
||||
assertTrue(used.get());
|
||||
|
||||
this.inputChannel.send(new GenericMessage<Object>(1000));
|
||||
Message<?> discarded = this.discardChannel.receive(5000);
|
||||
assertNotNull(discarded);
|
||||
assertEquals("Discarded: 1000", discarded.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBridge() {
|
||||
GenericMessage<String> message = new GenericMessage<>("test");
|
||||
this.bridgeFlowInput.send(message);
|
||||
Message<?> reply = this.bridgeFlowOutput.receive(5000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("test", reply.getPayload());
|
||||
|
||||
assertTrue(this.beanFactory.containsBean("bridgeFlow2.channel#0"));
|
||||
assertThat(this.beanFactory.getBean("bridgeFlow2.channel#0"), instanceOf(FixedSubscriberChannel.class));
|
||||
|
||||
try {
|
||||
this.bridgeFlow2Input.send(message);
|
||||
fail("Expected MessageDispatchingException");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(MessageDeliveryException.class));
|
||||
assertThat(e.getCause(), instanceOf(MessageDispatchingException.class));
|
||||
assertThat(e.getMessage(), containsString("Dispatcher has no subscribers"));
|
||||
}
|
||||
this.controlBus.send("@bridge.start()");
|
||||
this.bridgeFlow2Input.send(message);
|
||||
reply = this.bridgeFlow2Output.receive(5000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("test", reply.getPayload());
|
||||
assertTrue(this.delayedAdvice.getInvoked());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongLastMessageChannel() {
|
||||
ConfigurableApplicationContext context = null;
|
||||
try {
|
||||
context = new AnnotationConfigApplicationContext(InvalidLastMessageChannelFlowContext.class);
|
||||
fail("BeanCreationException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(BeanCreationException.class));
|
||||
assertThat(e.getMessage(), containsString("'.fixedSubscriberChannel()' " +
|
||||
"can't be the last EIP-method in the IntegrationFlow definition"));
|
||||
}
|
||||
finally {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodInvokingMessageHandler() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("world")
|
||||
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
|
||||
.build();
|
||||
this.methodInvokingInput.send(message);
|
||||
Message<?> receive = replyChannel.receive(5000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("Hello World and world", receive.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLambdas() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("World")
|
||||
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
|
||||
.build();
|
||||
this.lambdasInput.send(message);
|
||||
Message<?> receive = replyChannel.receive(5000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("Hello World", receive.getPayload());
|
||||
|
||||
message = MessageBuilder.withPayload("Spring")
|
||||
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
|
||||
.build();
|
||||
|
||||
this.lambdasInput.send(message);
|
||||
assertNull(replyChannel.receive(10));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClaimCheck() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
|
||||
Message<String> message = MutableMessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build();
|
||||
|
||||
this.claimCheckInput.send(message);
|
||||
|
||||
Message<?> receive = replyChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertSame(message, receive);
|
||||
|
||||
assertEquals(1, this.messageStore.getMessageCount());
|
||||
assertSame(message, this.messageStore.getMessage(message.getHeaders().getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGatewayFlow() throws Exception {
|
||||
PollableChannel replyChannel = new QueueChannel();
|
||||
Message<String> message = MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build();
|
||||
|
||||
this.gatewayInput.send(message);
|
||||
|
||||
Message<?> receive = replyChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("From Gateway SubFlow: FOO", receive.getPayload());
|
||||
assertNull(this.gatewayError.receive(1));
|
||||
|
||||
message = MessageBuilder.withPayload("bar").setReplyChannel(replyChannel).build();
|
||||
|
||||
this.gatewayInput.send(message);
|
||||
|
||||
receive = replyChannel.receive(1);
|
||||
assertNull(receive);
|
||||
|
||||
receive = this.gatewayError.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive, instanceOf(ErrorMessage.class));
|
||||
assertThat(receive.getPayload(), instanceOf(MessageRejectedException.class));
|
||||
assertThat(((Exception) receive.getPayload()).getMessage(), containsString("' rejected Message"));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private SubscribableChannel tappedChannel1;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("wireTapFlow2.input")
|
||||
private SubscribableChannel tappedChannel2;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("wireTapFlow3.input")
|
||||
private SubscribableChannel tappedChannel3;
|
||||
|
||||
@Autowired
|
||||
private SubscribableChannel tappedChannel4;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("tapChannel")
|
||||
private QueueChannel tapChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("wireTapFlow5.input")
|
||||
private SubscribableChannel tappedChannel5;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel wireTapSubflowResult;
|
||||
|
||||
@Test
|
||||
public void testWireTap() {
|
||||
this.tappedChannel1.send(new GenericMessage<>("foo"));
|
||||
this.tappedChannel1.send(new GenericMessage<>("bar"));
|
||||
Message<?> out = this.tapChannel.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("foo", out.getPayload());
|
||||
assertNull(this.tapChannel.receive(0));
|
||||
|
||||
this.tappedChannel2.send(new GenericMessage<>("foo"));
|
||||
this.tappedChannel2.send(new GenericMessage<>("bar"));
|
||||
out = this.tapChannel.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("foo", out.getPayload());
|
||||
assertNull(this.tapChannel.receive(0));
|
||||
|
||||
this.tappedChannel3.send(new GenericMessage<>("foo"));
|
||||
this.tappedChannel3.send(new GenericMessage<>("bar"));
|
||||
out = this.tapChannel.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("foo", out.getPayload());
|
||||
assertNull(this.tapChannel.receive(0));
|
||||
|
||||
this.tappedChannel4.send(new GenericMessage<>("foo"));
|
||||
this.tappedChannel4.send(new GenericMessage<>("bar"));
|
||||
out = this.tapChannel.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("foo", out.getPayload());
|
||||
out = this.tapChannel.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("bar", out.getPayload());
|
||||
|
||||
this.tappedChannel5.send(new GenericMessage<>("foo"));
|
||||
out = this.wireTapSubflowResult.receive(10000);
|
||||
assertNotNull(out);
|
||||
assertEquals("FOO", out.getPayload());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("subscribersFlow.input")
|
||||
private MessageChannel subscribersFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("subscriber1Results")
|
||||
private PollableChannel subscriber1Results;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("subscriber2Results")
|
||||
private PollableChannel subscriber2Results;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("subscriber3Results")
|
||||
private PollableChannel subscriber3Results;
|
||||
|
||||
@Test
|
||||
public void testSubscribersSubFlows() {
|
||||
this.subscribersFlowInput.send(new GenericMessage<>(2));
|
||||
|
||||
Message<?> receive1 = this.subscriber1Results.receive(5000);
|
||||
assertNotNull(receive1);
|
||||
assertEquals(1, receive1.getPayload());
|
||||
|
||||
Message<?> receive2 = this.subscriber2Results.receive(5000);
|
||||
assertNotNull(receive2);
|
||||
assertEquals(4, receive2.getPayload());
|
||||
Message<?> receive3 = this.subscriber3Results.receive(5000);
|
||||
assertNotNull(receive3);
|
||||
assertEquals(6, receive3.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@MessagingGateway(defaultRequestChannel = "controlBus")
|
||||
private interface ControlBusGateway {
|
||||
|
||||
void send(String command);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@IntegrationComponentScan
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow controlBusFlow() {
|
||||
return IntegrationFlows.from("controlBus").controlBus().get();
|
||||
}
|
||||
|
||||
@Bean(name = PollerMetadata.DEFAULT_POLLER)
|
||||
public PollerMetadata poller() {
|
||||
return Pollers.fixedRate(500).get();
|
||||
}
|
||||
|
||||
@Bean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
|
||||
public TaskScheduler taskScheduler() {
|
||||
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPoolSize(100);
|
||||
return threadPoolTaskScheduler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel inputChannel() {
|
||||
return MessageChannels.direct().get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel foo() {
|
||||
return MessageChannels.publishSubscribe().get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
public static class ContextConfiguration2 {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inputChannel")
|
||||
private MessageChannel inputChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("successChannel")
|
||||
private PollableChannel successChannel;
|
||||
|
||||
|
||||
@Bean
|
||||
public Advice expressionAdvice() {
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
advice.setOnSuccessExpression("payload");
|
||||
advice.setSuccessChannel(this.successChannel);
|
||||
return advice;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow flow2() {
|
||||
return IntegrationFlows.from(this.inputChannel)
|
||||
.filter(p -> p instanceof String, e -> e
|
||||
.id("filter")
|
||||
.discardFlow(df -> df
|
||||
.transform(String.class, "Discarded: "::concat)
|
||||
.channel(MessageChannels.queue("discardChannel"))))
|
||||
.channel("foo")
|
||||
.fixedSubscriberChannel()
|
||||
.<String, Integer>transform(Integer::parseInt)
|
||||
.transform(new PayloadSerializingTransformer(),
|
||||
c -> c.autoStartup(false).id("payloadSerializingTransformer"))
|
||||
.channel(MessageChannels.queue(new SimpleMessageStore(), "fooQueue"))
|
||||
.transform(new PayloadDeserializingTransformer())
|
||||
.filter("true", e -> e.id("expressionFilter"))
|
||||
.channel(publishSubscribeChannel())
|
||||
.transform((Integer p) -> p * 2, c -> c.advice(this.expressionAdvice()))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel publishSubscribeChannel() {
|
||||
return MessageChannels.publishSubscribe().get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow subscribersFlow() {
|
||||
return flow -> flow
|
||||
.publishSubscribeChannel(Executors.newCachedThreadPool(), s -> s
|
||||
.subscribe(f -> f
|
||||
.<Integer>handle((p, h) -> p / 2)
|
||||
.channel(MessageChannels.queue("subscriber1Results")))
|
||||
.subscribe(f -> f
|
||||
.<Integer>handle((p, h) -> p * 2)
|
||||
.channel(MessageChannels.queue("subscriber2Results"))))
|
||||
.<Integer>handle((p, h) -> p * 3)
|
||||
.channel(MessageChannels.queue("subscriber3Results"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wireTapFlow1() {
|
||||
return IntegrationFlows.from("tappedChannel1")
|
||||
.wireTap("tapChannel", wt -> wt.selector(m -> m.getPayload().equals("foo")))
|
||||
.channel("nullChannel")
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wireTapFlow2() {
|
||||
return f -> f
|
||||
.wireTap("tapChannel", wt -> wt.selector(m -> m.getPayload().equals("foo")))
|
||||
.channel("nullChannel");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wireTapFlow3() {
|
||||
return f -> f
|
||||
.transform("payload")
|
||||
.wireTap("tapChannel", wt -> wt.selector("payload == 'foo'"))
|
||||
.channel("nullChannel");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wireTapFlow4() {
|
||||
return IntegrationFlows.from("tappedChannel4")
|
||||
.wireTap(tapChannel())
|
||||
.channel("nullChannel")
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wireTapFlow5() {
|
||||
return f -> f
|
||||
.wireTap(sf -> sf
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.channel(MessageChannels.queue("wireTapSubflowResult")))
|
||||
.channel("nullChannel");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public QueueChannel tapChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
public static class AnnotationTestService {
|
||||
|
||||
@ServiceActivator(inputChannel = "publishSubscribeChannel")
|
||||
public void handle(Object payload) {
|
||||
assertEquals(100, payload);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ContextConfiguration3 {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("delayedAdvice")
|
||||
private MethodInterceptor delayedAdvice;
|
||||
|
||||
@Bean
|
||||
public QueueChannel successChannel() {
|
||||
return MessageChannels.queue().get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow bridgeFlow() {
|
||||
return IntegrationFlows.from(MessageChannels.queue("bridgeFlowInput"))
|
||||
.channel(MessageChannels.queue("bridgeFlowOutput"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow bridgeFlow2() {
|
||||
return IntegrationFlows.from("bridgeFlow2Input")
|
||||
.bridge(c -> c.autoStartup(false).id("bridge"))
|
||||
.fixedSubscriberChannel()
|
||||
.delay("delayer", d -> d
|
||||
.delayExpression("200")
|
||||
.advice(this.delayedAdvice)
|
||||
.messageStore(this.messageStore()))
|
||||
.channel(MessageChannels.queue("bridgeFlow2Output"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleMessageStore messageStore() {
|
||||
return new SimpleMessageStore();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow claimCheckFlow() {
|
||||
return IntegrationFlows.from("claimCheckInput")
|
||||
.claimCheckIn(this.messageStore())
|
||||
.claimCheckOut(this.messageStore())
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component("delayedAdvice")
|
||||
public static class DelayedAdvice implements MethodInterceptor {
|
||||
|
||||
private final AtomicBoolean invoked = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
this.invoked.set(true);
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
public Boolean getInvoked() {
|
||||
return invoked.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ContextConfiguration4 {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("integrationFlowTests.GreetingService")
|
||||
private MessageHandler greetingService;
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow methodInvokingFlow() {
|
||||
return IntegrationFlows.from("methodInvokingInput")
|
||||
.handle(this.greetingService)
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow lambdasFlow() {
|
||||
return IntegrationFlows.from("lambdasInput")
|
||||
.filter("World"::equals)
|
||||
.transform("Hello "::concat)
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow gatewayFlow() {
|
||||
return IntegrationFlows.from("gatewayInput")
|
||||
.gateway("gatewayRequest", g -> g.errorChannel("gatewayError").replyTimeout(10L))
|
||||
.gateway(f -> f.transform("From Gateway SubFlow: "::concat))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow gatewayRequestFlow() {
|
||||
return IntegrationFlows.from("gatewayRequest")
|
||||
.filter("foo"::equals, f -> f.throwExceptionOnRejection(true))
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel gatewayError() {
|
||||
return MessageChannels.queue().get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Service
|
||||
public static class GreetingService extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
@Autowired
|
||||
private WorldService worldService;
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return "Hello " + this.worldService.world() + " and " + requestMessage.getPayload();
|
||||
}
|
||||
}
|
||||
|
||||
@Service
|
||||
public static class WorldService {
|
||||
|
||||
public String world() {
|
||||
return "World";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class InvalidLastMessageChannelFlowContext {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wrongLastComponent() {
|
||||
return IntegrationFlows.from(MessageChannels.direct())
|
||||
.fixedSubscriberChannel()
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.flowservices;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.CorrelationStrategy;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.ReleaseStrategy;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlowAdapter;
|
||||
import org.springframework.integration.dsl.IntegrationFlowDefinition;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.scheduling.TriggerContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class FlowServiceTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("flowServiceTests.MyFlow.input")
|
||||
private MessageChannel input;
|
||||
|
||||
@Autowired(required = false)
|
||||
private MyFlow myFlow;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel myFlowAdapterOutput;
|
||||
|
||||
@Test
|
||||
public void testFlowService() {
|
||||
assertNotNull(this.myFlow);
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
this.input.send(MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build());
|
||||
Message<?> receive = replyChannel.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("FOO", receive.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlowAdapterService() {
|
||||
Message<?> receive = this.myFlowAdapterOutput.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("bar:FOO", receive.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("testGateway.input")
|
||||
private MessageChannel testGatewayInput;
|
||||
|
||||
@Test
|
||||
public void testGatewayExplicitReplyChannel() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
this.testGatewayInput.send(MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build());
|
||||
Message<?> message = replyChannel.receive(10000);
|
||||
assertNotNull(message);
|
||||
assertEquals("FOO", message.getPayload());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@ComponentScan
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow testGateway() {
|
||||
return f -> f.gateway("processChannel", g -> g.replyChannel("replyChannel"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow subFlow() {
|
||||
return IntegrationFlows
|
||||
.from("processChannel")
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.channel("replyChannel")
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class MyFlow implements IntegrationFlow {
|
||||
|
||||
@Override
|
||||
public void configure(IntegrationFlowDefinition<?> f) {
|
||||
f.<String, String>transform(String::toUpperCase);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class MyFlowAdapter extends IntegrationFlowAdapter {
|
||||
|
||||
private final AtomicReference<Date> executionDate = new AtomicReference<>(new Date());
|
||||
|
||||
private Date nextExecutionTime(TriggerContext triggerContext) {
|
||||
return this.executionDate.getAndSet(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IntegrationFlowDefinition<?> buildFlow() {
|
||||
return from(this, "messageSource", e -> e.poller(p -> p.trigger(this::nextExecutionTime)))
|
||||
.split(this, null, e -> e.applySequence(false))
|
||||
.transform(this)
|
||||
.aggregate(a -> a.processor(this, null))
|
||||
.enrichHeaders(Collections.singletonMap("foo", "FOO"))
|
||||
.filter(this)
|
||||
.handle(this)
|
||||
.channel(MessageChannels.queue("myFlowAdapterOutput"));
|
||||
}
|
||||
|
||||
public String messageSource() {
|
||||
return "B,A,R";
|
||||
}
|
||||
|
||||
@Splitter
|
||||
public String[] split(String payload) {
|
||||
return StringUtils.commaDelimitedListToStringArray(payload);
|
||||
}
|
||||
|
||||
@Transformer
|
||||
public String transform(String payload) {
|
||||
return payload.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
@CorrelationStrategy
|
||||
public Integer correlationKey() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ReleaseStrategy
|
||||
public boolean canRelease(Collection<Message<?>> messages) {
|
||||
return messages.size() == 3;
|
||||
}
|
||||
|
||||
@Aggregator
|
||||
public String aggregate(List<String> payloads) {
|
||||
return payloads.stream().collect(Collectors.joining());
|
||||
}
|
||||
|
||||
@Filter
|
||||
public boolean filter(@Header Optional<String> foo) {
|
||||
return foo.isPresent();
|
||||
}
|
||||
|
||||
@ServiceActivator
|
||||
public String handle(String payload, @Header String foo) {
|
||||
return payload + ":" + foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.manualflow;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlowAdapter;
|
||||
import org.springframework.integration.dsl.IntegrationFlowDefinition;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.dsl.context.IntegrationFlowContext;
|
||||
import org.springframework.integration.dsl.context.IntegrationFlowRegistration;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@ContextConfiguration(classes = ManualFlowTests.RootConfiguration.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class ManualFlowTests {
|
||||
|
||||
@Autowired
|
||||
private IntegrationFlowContext integrationFlowContext;
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Test
|
||||
public void testManualFlowRegistration() throws InterruptedException {
|
||||
IntegrationFlow myFlow = f -> f
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.channel(MessageChannels.queue())
|
||||
.transform("Hello, "::concat, e -> e
|
||||
.poller(p -> p
|
||||
.fixedDelay(10)
|
||||
.maxMessagesPerPoll(1)
|
||||
.receiveTimeout(10)))
|
||||
.handle(new BeanFactoryHandler());
|
||||
|
||||
BeanFactoryHandler additionalBean = new BeanFactoryHandler();
|
||||
IntegrationFlowRegistration flowRegistration =
|
||||
this.integrationFlowContext.registration(myFlow)
|
||||
.addBean(additionalBean)
|
||||
.register();
|
||||
|
||||
BeanFactoryHandler bean =
|
||||
this.beanFactory.getBean(flowRegistration.getId() + BeanFactoryHandler.class.getName() + "#0",
|
||||
BeanFactoryHandler.class);
|
||||
assertSame(additionalBean, bean);
|
||||
assertSame(this.beanFactory, bean.beanFactory);
|
||||
|
||||
MessagingTemplate messagingTemplate = flowRegistration.getMessagingTemplate();
|
||||
messagingTemplate.setReceiveTimeout(10000);
|
||||
|
||||
assertEquals("Hello, FOO", messagingTemplate.convertSendAndReceive("foo", String.class));
|
||||
|
||||
assertEquals("Hello, BAR", messagingTemplate.convertSendAndReceive("bar", String.class));
|
||||
|
||||
try {
|
||||
messagingTemplate.receive();
|
||||
fail("UnsupportedOperationException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(UnsupportedOperationException.class));
|
||||
assertThat(e.getMessage(), containsString("The 'receive()/receiveAndConvert()' isn't supported"));
|
||||
}
|
||||
|
||||
flowRegistration.destroy();
|
||||
|
||||
assertFalse(this.beanFactory.containsBean(flowRegistration.getId()));
|
||||
assertFalse(this.beanFactory.containsBean(flowRegistration.getId() + ".input"));
|
||||
assertFalse(this.beanFactory.containsBean(flowRegistration.getId() + BeanFactoryHandler.class.getName() + "#0"));
|
||||
|
||||
ThreadPoolTaskScheduler taskScheduler = this.beanFactory.getBean(ThreadPoolTaskScheduler.class);
|
||||
Thread.sleep(100);
|
||||
assertEquals(0, taskScheduler.getActiveCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongLifecycle() {
|
||||
|
||||
class MyIntegrationFlow implements IntegrationFlow {
|
||||
|
||||
@Override
|
||||
public void configure(IntegrationFlowDefinition<?> flow) {
|
||||
flow.bridge(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IntegrationFlow testFlow = new MyIntegrationFlow();
|
||||
|
||||
// This is fine because we are not going to start it automatically.
|
||||
assertNotNull(this.integrationFlowContext.registration(testFlow)
|
||||
.autoStartup(false)
|
||||
.register());
|
||||
|
||||
try {
|
||||
this.integrationFlowContext.registration(testFlow).register();
|
||||
fail("IllegalStateException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(IllegalStateException.class));
|
||||
assertThat(e.getMessage(), containsString("Consider to implement it for [" + testFlow + "]."));
|
||||
}
|
||||
|
||||
try {
|
||||
this.integrationFlowContext.remove("foo");
|
||||
fail("IllegalStateException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(IllegalStateException.class));
|
||||
assertThat(e.getMessage(), containsString("But [" + "foo" + "] ins't one of them."));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicSubFlow() {
|
||||
PollableChannel resultChannel = new QueueChannel();
|
||||
|
||||
this.integrationFlowContext.registration(flow ->
|
||||
flow.publishSubscribeChannel(p -> p
|
||||
.minSubscribers(1)
|
||||
.subscribe(f -> f.channel(resultChannel))
|
||||
))
|
||||
.id("dynamicFlow")
|
||||
.register();
|
||||
|
||||
this.integrationFlowContext.messagingTemplateFor("dynamicFlow").send(new GenericMessage<>("test"));
|
||||
|
||||
Message<?> receive = resultChannel.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("test", receive.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicAdapterFlow() {
|
||||
this.integrationFlowContext.registration(new MyFlowAdapter()).register();
|
||||
PollableChannel resultChannel = this.beanFactory.getBean("flowAdapterOutput", PollableChannel.class);
|
||||
|
||||
Message<?> receive = resultChannel.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("flowAdapterMessage", receive.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWrongIntegrationFlowScope() {
|
||||
try {
|
||||
new AnnotationConfigApplicationContext(InvalidIntegrationFlowScopeConfiguration.class).close();
|
||||
fail("BeanCreationNotAllowedException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(BeanCreationNotAllowedException.class));
|
||||
assertThat(e.getMessage(), containsString("IntegrationFlows can not be scoped beans."));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class RootConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public Date foo() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MyFlowAdapter extends IntegrationFlowAdapter {
|
||||
|
||||
private final AtomicReference<Date> nextExecutionTime = new AtomicReference<>(new Date());
|
||||
|
||||
@Override
|
||||
protected IntegrationFlowDefinition<?> buildFlow() {
|
||||
return from(() -> new GenericMessage<>("flowAdapterMessage"),
|
||||
e -> e.poller(p -> p
|
||||
.trigger(ctx -> this.nextExecutionTime.getAndSet(null))))
|
||||
.channel(MessageChannels.queue("flowAdapterOutput"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class InvalidIntegrationFlowScopeConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public IntegrationFlow wrongScopeFlow() {
|
||||
return flow -> flow.bridge(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class BeanFactoryHandler extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Objects.requireNonNull(this.beanFactory);
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
this.beanFactory.getClass(); // ensure wiring before afterPropertiesSet()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.reactivestreams;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class ReactiveStreamsTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveFlow")
|
||||
private Publisher<Message<String>> publisher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("pollableReactiveFlow")
|
||||
private Publisher<Message<Integer>> pollablePublisher;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("reactiveSteamsMessageSource")
|
||||
private Lifecycle messageSource;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inputChannel")
|
||||
private MessageChannel inputChannel;
|
||||
|
||||
@Test
|
||||
public void testReactiveFlow() throws InterruptedException {
|
||||
List<String> results = new ArrayList<>();
|
||||
CountDownLatch latch = new CountDownLatch(6);
|
||||
Flux.from(this.publisher)
|
||||
.map(m -> m.getPayload().toUpperCase())
|
||||
.subscribe(p -> {
|
||||
results.add(p);
|
||||
latch.countDown();
|
||||
});
|
||||
this.messageSource.start();
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
String[] strings = results.toArray(new String[results.size()]);
|
||||
assertArrayEquals(new String[] { "A", "B", "C", "D", "E", "F" }, strings);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Until Reactor 3.0.x solution")
|
||||
public void testPollableReactiveFlow() throws InterruptedException, TimeoutException, ExecutionException {
|
||||
this.inputChannel.send(new GenericMessage<>("1,2,3,4,5"));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(6);
|
||||
|
||||
Flux.from(this.pollablePublisher)
|
||||
.filter(m -> m.getHeaders().containsKey(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER))
|
||||
.doOnNext(p -> latch.countDown())
|
||||
.subscribe(6);
|
||||
|
||||
Future<List<Integer>> future =
|
||||
Executors.newSingleThreadExecutor().submit(() ->
|
||||
Flux.fromArray(new String[] { "11,12,13" })
|
||||
.map(v -> v.split(","))
|
||||
.map(Arrays::asList)
|
||||
.flatMapIterable(data -> data)
|
||||
.map(Integer::parseInt)
|
||||
.<Message<Integer>>map(GenericMessage<Integer>::new)
|
||||
.concatWith(this.pollablePublisher)
|
||||
.map(Message::getPayload)
|
||||
.collect(Collectors.toList())
|
||||
.block(Duration.ofSeconds(5)));
|
||||
|
||||
this.inputChannel.send(new GenericMessage<>("6,7,8,9,10"));
|
||||
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
List<Integer> integers = future.get(10, TimeUnit.SECONDS);
|
||||
assertNotNull(integers);
|
||||
assertEquals(7, integers.size());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration {
|
||||
|
||||
private final AtomicBoolean invoked = new AtomicBoolean();
|
||||
|
||||
@Bean
|
||||
public Publisher<Message<String>> reactiveFlow() {
|
||||
return IntegrationFlows
|
||||
.from(() -> new GenericMessage<>("a,b,c,d,e,f"),
|
||||
e -> e.poller(p -> p.trigger(ctx -> this.invoked.getAndSet(true) ? null : new Date()))
|
||||
.autoStartup(false)
|
||||
.id("reactiveSteamsMessageSource"))
|
||||
.split(String.class, p -> p.split(","))
|
||||
.toReactivePublisher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Publisher<Message<Integer>> pollableReactiveFlow() {
|
||||
return IntegrationFlows
|
||||
.from("inputChannel")
|
||||
.split(s -> s.delimiters(","))
|
||||
.<String, Integer>transform(Integer::parseInt)
|
||||
.channel(MessageChannels.queue())
|
||||
.toReactivePublisher();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
/*
|
||||
* Copyright 2016 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.dsl.routers;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.EnableMessageHistory;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.core.DestinationResolutionException;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class RouterTests {
|
||||
|
||||
@Autowired
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerInput")
|
||||
private MessageChannel routerInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("oddChannel")
|
||||
private PollableChannel oddChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("evenChannel")
|
||||
private PollableChannel evenChannel;
|
||||
|
||||
|
||||
@Test
|
||||
public void testRouter() {
|
||||
this.beanFactory.containsBean("routeFlow.subFlow#0.channel#0");
|
||||
|
||||
int[] payloads = new int[] {1, 2, 3, 4, 5, 6};
|
||||
|
||||
for (int payload : payloads) {
|
||||
this.routerInput.send(new GenericMessage<>(payload));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Message<?> receive = this.oddChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertEquals(payloads[i * 2] * 3, receive.getPayload());
|
||||
|
||||
receive = this.evenChannel.receive(2000);
|
||||
assertNotNull(receive);
|
||||
assertEquals(payloads[i * 2 + 1], receive.getPayload());
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerTwoSubFlows.input")
|
||||
private MessageChannel routerTwoSubFlowsInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerTwoSubFlowsOutput")
|
||||
private PollableChannel routerTwoSubFlowsOutput;
|
||||
|
||||
@Test
|
||||
public void testRouterWithTwoSubflows() {
|
||||
this.routerTwoSubFlowsInput.send(new GenericMessage<Object>(Arrays.asList(1, 2, 3, 4, 5, 6)));
|
||||
Message<?> receive = this.routerTwoSubFlowsOutput.receive(5000);
|
||||
assertNotNull(receive);
|
||||
Object payload = receive.getPayload();
|
||||
assertThat(payload, instanceOf(List.class));
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Integer> results = (List<Integer>) payload;
|
||||
|
||||
assertArrayEquals(new Integer[] {3, 4, 9, 8, 15, 12}, results.toArray(new Integer[results.size()]));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routeSubflowToReplyChannelFlow.input")
|
||||
private MessageChannel routeSubflowToReplyChannelFlowInput;
|
||||
|
||||
@Test
|
||||
public void testRouterSubflowWithReplyChannelHeader() {
|
||||
PollableChannel replyChannel = new QueueChannel();
|
||||
this.routeSubflowToReplyChannelFlowInput.send(
|
||||
MessageBuilder.withPayload("baz")
|
||||
.setReplyChannel(replyChannel)
|
||||
.build());
|
||||
|
||||
Message<?> receive = replyChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("BAZ", receive.getPayload());
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routeSubflowWithoutReplyToMainFlow.input")
|
||||
private MessageChannel routeSubflowWithoutReplyToMainFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerSubflowResult")
|
||||
private PollableChannel routerSubflowResult;
|
||||
|
||||
@Test
|
||||
public void testRouterSubflowWithoutReplyToMainFlow() {
|
||||
this.routeSubflowWithoutReplyToMainFlowInput.send(new GenericMessage<>("BOO"));
|
||||
|
||||
Message<?> receive = routerSubflowResult.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("boo", receive.getPayload());
|
||||
assertNull(this.defaultOutputChannel.receive(1));
|
||||
this.routeSubflowWithoutReplyToMainFlowInput.send(new GenericMessage<>("foo"));
|
||||
assertNotNull(this.defaultOutputChannel.receive(10000));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListInput")
|
||||
private MessageChannel recipientListInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("foo-channel")
|
||||
private PollableChannel fooChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bar-channel")
|
||||
private PollableChannel barChannel;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListSubFlow1Result")
|
||||
private PollableChannel recipientListSubFlow1Result;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListSubFlow2Result")
|
||||
private PollableChannel recipientListSubFlow2Result;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListSubFlow3Result")
|
||||
private PollableChannel recipientListSubFlow3Result;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("defaultOutputChannel")
|
||||
private PollableChannel defaultOutputChannel;
|
||||
|
||||
@Test
|
||||
public void testRecipientListRouter() {
|
||||
|
||||
Message<String> fooMessage = MessageBuilder.withPayload("fooPayload").setHeader("recipient", true).build();
|
||||
Message<String> barMessage = MessageBuilder.withPayload("barPayload").setHeader("recipient", true).build();
|
||||
Message<String> bazMessage = new GenericMessage<>("baz");
|
||||
Message<String> badMessage = new GenericMessage<>("badPayload");
|
||||
|
||||
this.recipientListInput.send(fooMessage);
|
||||
Message<?> result1a = this.fooChannel.receive(10000);
|
||||
assertNotNull(result1a);
|
||||
assertEquals("foo", result1a.getPayload());
|
||||
Message<?> result1b = this.barChannel.receive(10000);
|
||||
assertNotNull(result1b);
|
||||
assertEquals("foo", result1b.getPayload());
|
||||
Message<?> result1c = this.recipientListSubFlow1Result.receive(10000);
|
||||
assertNotNull(result1c);
|
||||
assertEquals("FOO", result1c.getPayload());
|
||||
assertNull(this.recipientListSubFlow2Result.receive(0));
|
||||
|
||||
this.recipientListInput.send(barMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
assertNull(this.recipientListSubFlow2Result.receive(0));
|
||||
Message<?> result2b = this.barChannel.receive(10000);
|
||||
assertNotNull(result2b);
|
||||
assertEquals("bar", result2b.getPayload());
|
||||
Message<?> result2c = this.recipientListSubFlow1Result.receive(10000);
|
||||
assertNotNull(result1c);
|
||||
assertEquals("BAR", result2c.getPayload());
|
||||
|
||||
this.recipientListInput.send(bazMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
assertNull(this.barChannel.receive(0));
|
||||
Message<?> result3c = this.recipientListSubFlow1Result.receive(10000);
|
||||
assertNotNull(result3c);
|
||||
assertEquals("BAZ", result3c.getPayload());
|
||||
Message<?> result4c = this.recipientListSubFlow2Result.receive(10000);
|
||||
assertNotNull(result4c);
|
||||
assertEquals("Hello baz", result4c.getPayload());
|
||||
|
||||
this.recipientListInput.send(badMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
assertNull(this.barChannel.receive(0));
|
||||
assertNull(this.recipientListSubFlow1Result.receive(0));
|
||||
assertNull(this.recipientListSubFlow2Result.receive(0));
|
||||
Message<?> resultD = this.defaultOutputChannel.receive(10000);
|
||||
assertNotNull(resultD);
|
||||
assertEquals("bad", resultD.getPayload());
|
||||
|
||||
this.recipientListInput.send(new GenericMessage<>("bax"));
|
||||
Message<?> result5c = this.recipientListSubFlow3Result.receive(10000);
|
||||
assertNotNull(result5c);
|
||||
assertEquals("bax", result5c.getPayload());
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
assertNull(this.barChannel.receive(0));
|
||||
assertNull(this.recipientListSubFlow1Result.receive(0));
|
||||
assertNull(this.recipientListSubFlow2Result.receive(0));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerMethodInput")
|
||||
private MessageChannel routerMethodInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerMethod2Input")
|
||||
private MessageChannel routerMethod2Input;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routeMethodInvocationFlow3.input")
|
||||
private MessageChannel routerMethod3Input;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerMultiInput")
|
||||
private MessageChannel routerMultiInput;
|
||||
|
||||
@Test
|
||||
public void testMethodInvokingRouter() {
|
||||
Message<String> fooMessage = new GenericMessage<>("foo");
|
||||
Message<String> barMessage = new GenericMessage<>("bar");
|
||||
Message<String> badMessage = new GenericMessage<>("bad");
|
||||
|
||||
this.routerMethodInput.send(fooMessage);
|
||||
|
||||
Message<?> result1a = this.fooChannel.receive(2000);
|
||||
assertNotNull(result1a);
|
||||
assertEquals("foo", result1a.getPayload());
|
||||
assertNull(this.barChannel.receive(0));
|
||||
|
||||
this.routerMethodInput.send(barMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
Message<?> result2b = this.barChannel.receive(2000);
|
||||
assertNotNull(result2b);
|
||||
assertEquals("bar", result2b.getPayload());
|
||||
|
||||
try {
|
||||
this.routerMethodInput.send(badMessage);
|
||||
fail("MessageDeliveryException expected.");
|
||||
}
|
||||
catch (MessageDeliveryException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("No channel resolved by router"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodInvokingRouter2() {
|
||||
Message<String> fooMessage = MessageBuilder.withPayload("foo").setHeader("targetChannel", "foo").build();
|
||||
Message<String> barMessage = MessageBuilder.withPayload("bar").setHeader("targetChannel", "bar").build();
|
||||
Message<String> badMessage = MessageBuilder.withPayload("bad").setHeader("targetChannel", "bad").build();
|
||||
|
||||
this.routerMethod2Input.send(fooMessage);
|
||||
|
||||
Message<?> result1a = this.fooChannel.receive(2000);
|
||||
assertNotNull(result1a);
|
||||
assertEquals("foo", result1a.getPayload());
|
||||
assertNull(this.barChannel.receive(0));
|
||||
|
||||
this.routerMethod2Input.send(barMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
Message<?> result2b = this.barChannel.receive(2000);
|
||||
assertNotNull(result2b);
|
||||
assertEquals("bar", result2b.getPayload());
|
||||
|
||||
try {
|
||||
this.routerMethod2Input.send(badMessage);
|
||||
fail("DestinationResolutionException expected.");
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause(), instanceOf(DestinationResolutionException.class));
|
||||
assertThat(e.getCause().getMessage(),
|
||||
containsString("failed to look up MessageChannel with name 'bad-channel'"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodInvokingRouter3() {
|
||||
Message<String> fooMessage = new GenericMessage<>("foo");
|
||||
Message<String> barMessage = new GenericMessage<>("bar");
|
||||
Message<String> badMessage = new GenericMessage<>("bad");
|
||||
|
||||
this.routerMethod3Input.send(fooMessage);
|
||||
|
||||
Message<?> result1a = this.fooChannel.receive(2000);
|
||||
assertNotNull(result1a);
|
||||
assertEquals("foo", result1a.getPayload());
|
||||
assertNull(this.barChannel.receive(0));
|
||||
|
||||
this.routerMethod3Input.send(barMessage);
|
||||
assertNull(this.fooChannel.receive(0));
|
||||
Message<?> result2b = this.barChannel.receive(2000);
|
||||
assertNotNull(result2b);
|
||||
assertEquals("bar", result2b.getPayload());
|
||||
|
||||
try {
|
||||
this.routerMethod3Input.send(badMessage);
|
||||
fail("DestinationResolutionException expected.");
|
||||
}
|
||||
catch (MessagingException e) {
|
||||
assertThat(e.getCause(), instanceOf(DestinationResolutionException.class));
|
||||
assertThat(e.getCause().getMessage(),
|
||||
containsString("failed to look up MessageChannel with name 'bad-channel'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRouter() {
|
||||
|
||||
Message<String> fooMessage = new GenericMessage<>("foo");
|
||||
Message<String> barMessage = new GenericMessage<>("bar");
|
||||
Message<String> badMessage = new GenericMessage<>("bad");
|
||||
|
||||
this.routerMultiInput.send(fooMessage);
|
||||
Message<?> result1a = this.fooChannel.receive(2000);
|
||||
assertNotNull(result1a);
|
||||
assertEquals("foo", result1a.getPayload());
|
||||
Message<?> result1b = this.barChannel.receive(2000);
|
||||
assertNotNull(result1b);
|
||||
assertEquals("foo", result1b.getPayload());
|
||||
|
||||
this.routerMultiInput.send(barMessage);
|
||||
Message<?> result2a = this.fooChannel.receive(2000);
|
||||
assertNotNull(result2a);
|
||||
assertEquals("bar", result2a.getPayload());
|
||||
Message<?> result2b = this.barChannel.receive(2000);
|
||||
assertNotNull(result2b);
|
||||
assertEquals("bar", result2b.getPayload());
|
||||
|
||||
try {
|
||||
this.routerMultiInput.send(badMessage);
|
||||
fail("MessageDeliveryException expected.");
|
||||
}
|
||||
catch (MessageDeliveryException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("No channel resolved by router"));
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("payloadTypeRouteFlow.input")
|
||||
private MessageChannel payloadTypeRouteFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("stringsChannel")
|
||||
private PollableChannel stringsChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("integersChannel")
|
||||
private PollableChannel integersChannel;
|
||||
|
||||
@Test
|
||||
public void testPayloadTypeRouteFlow() {
|
||||
this.payloadTypeRouteFlowInput.send(new GenericMessage<>("foo"));
|
||||
this.payloadTypeRouteFlowInput.send(new GenericMessage<>(22));
|
||||
this.payloadTypeRouteFlowInput.send(new GenericMessage<>(33));
|
||||
this.payloadTypeRouteFlowInput.send(new GenericMessage<>("BAR"));
|
||||
|
||||
Message<?> receive = this.stringsChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("foo", receive.getPayload());
|
||||
|
||||
receive = this.stringsChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("BAR", receive.getPayload());
|
||||
|
||||
assertNull(this.stringsChannel.receive(10));
|
||||
|
||||
receive = this.integersChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals(22, receive.getPayload());
|
||||
|
||||
receive = this.integersChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals(33, receive.getPayload());
|
||||
|
||||
assertNull(this.integersChannel.receive(10));
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListOrderFlow.input")
|
||||
private MessageChannel recipientListOrderFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("recipientListOrderResult")
|
||||
private PollableChannel recipientListOrderResult;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testRecipientListRouterOrder() {
|
||||
this.recipientListOrderFlowInput.send(new GenericMessage<>(new AtomicReference<>("")));
|
||||
Message<?> receive = this.recipientListOrderResult.receive(10000);
|
||||
assertNotNull(receive);
|
||||
|
||||
AtomicReference<String> result = (AtomicReference<String>) receive.getPayload();
|
||||
assertEquals("Hello World", result.get());
|
||||
|
||||
receive = this.recipientListOrderResult.receive(10000);
|
||||
assertNotNull(receive);
|
||||
result = (AtomicReference<String>) receive.getPayload();
|
||||
assertEquals("Hello World", result.get());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerAsNonLastFlow.input")
|
||||
private MessageChannel routerAsNonLastFlowChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("routerAsNonLastDefaultOutputChannel")
|
||||
private PollableChannel routerAsNonLastDefaultOutputChannel;
|
||||
|
||||
@Test
|
||||
public void testRouterAsNonLastComponent() {
|
||||
this.routerAsNonLastFlowChannel.send(new GenericMessage<>("Hello World"));
|
||||
Message<?> receive = this.routerAsNonLastDefaultOutputChannel.receive(1000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("Hello World", receive.getPayload());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("scatterGatherFlow.input")
|
||||
private MessageChannel scatterGatherFlowInput;
|
||||
|
||||
@Test
|
||||
public void testScatterGather() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<String> request = MessageBuilder.withPayload("foo")
|
||||
.setReplyChannel(replyChannel)
|
||||
.build();
|
||||
this.scatterGatherFlowInput.send(request);
|
||||
Message<?> bestQuoteMessage = replyChannel.receive(10000);
|
||||
assertNotNull(bestQuoteMessage);
|
||||
Object payload = bestQuoteMessage.getPayload();
|
||||
assertThat(payload, instanceOf(List.class));
|
||||
assertThat(((List<?>) payload).size(), greaterThanOrEqualTo(1));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@EnableMessageHistory({ "recipientListOrder*", "recipient1*", "recipient2*" })
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public QueueChannel evenChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeFlow() {
|
||||
return IntegrationFlows.from("routerInput")
|
||||
.<Integer, Boolean>route(p -> p % 2 == 0,
|
||||
m -> m.channelMapping(true, "evenChannel")
|
||||
.subFlowMapping(false, f ->
|
||||
f.<Integer>handle((p, h) -> p * 3))
|
||||
.defaultOutputToParentFlow())
|
||||
.channel(MessageChannels.queue("oddChannel"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeSubflowToReplyChannelFlow() {
|
||||
return f -> f
|
||||
.<Boolean>route("true", m -> m
|
||||
.subFlowMapping(true, sf -> sf
|
||||
.<String>handle((p, h) -> p.toUpperCase())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeSubflowWithoutReplyToMainFlow() {
|
||||
return f -> f
|
||||
.<String, Boolean>route("BOO"::equals, m -> m
|
||||
.resolutionRequired(false)
|
||||
.subFlowMapping(true, sf -> sf
|
||||
.transform(String.class, String::toLowerCase)
|
||||
.channel(MessageChannels.queue("routerSubflowResult")))
|
||||
.defaultSubFlowMapping(sf -> sf.channel("defaultOutputChannel")));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routerTwoSubFlows() {
|
||||
return f -> f
|
||||
.split()
|
||||
.<Integer, Boolean>route(p -> p % 2 == 0, m -> m
|
||||
.subFlowMapping(true, sf -> sf.<Integer>handle((p, h) -> p * 2))
|
||||
.subFlowMapping(false, sf -> sf.<Integer>handle((p, h) -> p * 3)))
|
||||
.aggregate()
|
||||
.channel(MessageChannels.queue("routerTwoSubFlowsOutput"));
|
||||
}
|
||||
|
||||
@Bean(name = "foo-channel")
|
||||
public MessageChannel fooChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean(name = "bar-channel")
|
||||
public MessageChannel barChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel defaultOutputChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow recipientListFlow() {
|
||||
return IntegrationFlows.from("recipientListInput")
|
||||
.<String, String>transform(p -> p.replaceFirst("Payload", ""))
|
||||
.routeToRecipients(r -> r
|
||||
.recipient("foo-channel", "'foo' == payload")
|
||||
.recipientMessageSelector("bar-channel", m ->
|
||||
m.getHeaders().containsKey("recipient")
|
||||
&& (boolean) m.getHeaders().get("recipient"))
|
||||
.recipientFlow("'foo' == payload or 'bar' == payload or 'baz' == payload",
|
||||
f -> f.<String, String>transform(String::toUpperCase)
|
||||
.channel(MessageChannels.queue("recipientListSubFlow1Result")))
|
||||
.recipientFlow((String p) -> p.startsWith("baz"),
|
||||
f -> f.transform("Hello "::concat)
|
||||
.channel(MessageChannels.queue("recipientListSubFlow2Result")))
|
||||
.recipientFlow(new FunctionExpression<Message<?>>(m -> "bax".equals(m.getPayload())),
|
||||
f -> f.channel(MessageChannels.queue("recipientListSubFlow3Result")))
|
||||
.defaultOutputToParentFlow())
|
||||
.channel("defaultOutputChannel")
|
||||
.get();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public RoutingTestBean routingTestBean() {
|
||||
return new RoutingTestBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeMethodInvocationFlow() {
|
||||
return IntegrationFlows.from("routerMethodInput")
|
||||
.route("routingTestBean", "routeMessage")
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeMethodInvocationFlow2() {
|
||||
return IntegrationFlows.from("routerMethod2Input")
|
||||
.route(new RoutingTestBean())
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeMethodInvocationFlow3() {
|
||||
return f -> f.route((String p) -> routingTestBean().routePayload(p));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routeMultiMethodInvocationFlow() {
|
||||
return IntegrationFlows.from("routerMultiInput")
|
||||
.route(String.class, p -> p.equals("foo") || p.equals("bar") ? new String[] {"foo", "bar"} : null,
|
||||
s -> s.suffix("-channel"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PollableChannel stringsChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PollableChannel integersChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow payloadTypeRouteFlow() {
|
||||
return f -> f
|
||||
.<Object, Class<?>>route(Object::getClass, m -> m
|
||||
.channelMapping(String.class, "stringsChannel")
|
||||
.channelMapping(Integer.class, "integersChannel"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routerAsNonLastFlow() {
|
||||
return f -> f
|
||||
.<String, String>route(p -> p, r ->
|
||||
r.resolutionRequired(false)
|
||||
.defaultOutputToParentFlow())
|
||||
.channel(MessageChannels.queue("routerAsNonLastDefaultOutputChannel"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow recipientListOrderFlow() {
|
||||
return f -> f
|
||||
.routeToRecipients(r -> r
|
||||
.recipient("recipient2.input")
|
||||
.recipient("recipient1.input"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow recipient1() {
|
||||
return f -> f
|
||||
.<AtomicReference<String>>handle((p, h) -> {
|
||||
p.set(p.get() + "World");
|
||||
return p;
|
||||
})
|
||||
.channel("recipientListOrderResult");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow recipient2() {
|
||||
return f -> f
|
||||
.<AtomicReference<String>>handle((p, h) -> {
|
||||
p.set(p.get() + "Hello ");
|
||||
return p;
|
||||
})
|
||||
.channel("recipientListOrderResult");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PollableChannel recipientListOrderResult() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow scatterGatherFlow() {
|
||||
return f -> f
|
||||
.scatterGather(scatterer -> scatterer
|
||||
.applySequence(true)
|
||||
.recipientFlow(m -> true, sf -> sf.handle((p, h) -> Math.random() * 10))
|
||||
.recipientFlow(m -> true, sf -> sf.handle((p, h) -> Math.random() * 10))
|
||||
.recipientFlow(m -> true, sf -> sf.handle((p, h) -> Math.random() * 10)),
|
||||
gatherer -> gatherer
|
||||
.releaseStrategy(group ->
|
||||
group.size() == 3 ||
|
||||
group.getMessages()
|
||||
.stream()
|
||||
.anyMatch(m -> (Double) m.getPayload() > 5)),
|
||||
scatterGather -> scatterGather
|
||||
.gatherTimeout(10_000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class RoutingTestBean {
|
||||
|
||||
RoutingTestBean() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String routePayload(String name) {
|
||||
return name + "-channel";
|
||||
}
|
||||
|
||||
@Router
|
||||
public String routeByHeader(@Header("targetChannel") String name) {
|
||||
return name + "-channel";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String routeMessage(Message<?> message) {
|
||||
if (message.getPayload().equals("foo")) {
|
||||
return "foo-channel";
|
||||
}
|
||||
else if (message.getPayload().equals("bar")) {
|
||||
return "bar-channel";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2016 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.transaction;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class TransactionInterceptorBuilderTests {
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager txm;
|
||||
|
||||
@Autowired
|
||||
private TransactionInterceptor interceptor1;
|
||||
|
||||
@Autowired
|
||||
private TransactionInterceptor interceptor2;
|
||||
|
||||
@Test
|
||||
public void test() throws Throwable {
|
||||
verify(this.interceptor1, this.txm);
|
||||
verify(this.interceptor2, null);
|
||||
}
|
||||
|
||||
private void verify(TransactionInterceptor interceptor, PlatformTransactionManager txm) {
|
||||
assertSame(txm, interceptor.getTransactionManager());
|
||||
TransactionAttribute atts = interceptor.getTransactionAttributeSource()
|
||||
.getTransactionAttribute(null, null);
|
||||
Assert.assertThat(atts.getPropagationBehavior(), equalTo(Propagation.REQUIRES_NEW.value()));
|
||||
Assert.assertThat(atts.getIsolationLevel(), equalTo(Isolation.SERIALIZABLE.value()));
|
||||
Assert.assertThat(atts.getTimeout(), equalTo(42));
|
||||
assertTrue(atts.isReadOnly());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public PseudoTransactionManager transactionManager() {
|
||||
return new PseudoTransactionManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionInterceptor interceptor1(PlatformTransactionManager transactionManager) {
|
||||
return new TransactionInterceptorBuilder()
|
||||
.propagation(Propagation.REQUIRES_NEW)
|
||||
.isolation(Isolation.SERIALIZABLE)
|
||||
.timeout(42)
|
||||
.readOnly(true)
|
||||
.transactionManager(transactionManager)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionInterceptor interceptor2() {
|
||||
return new TransactionInterceptorBuilder()
|
||||
.propagation(Propagation.REQUIRES_NEW)
|
||||
.isolation(Isolation.SERIALIZABLE)
|
||||
.timeout(42)
|
||||
.readOnly(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright 2016 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.event.dsl;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.channel.MessageChannels;
|
||||
import org.springframework.integration.event.core.MessagingEvent;
|
||||
import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer;
|
||||
import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class IntegrationFlowEventsTests {
|
||||
|
||||
private static MessageGroupStore messageGroupStore = new SimpleMessageStore();
|
||||
|
||||
private static String GROUP_ID = "testGroup";
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
messageGroupStore.addMessageToGroup(GROUP_ID, new GenericMessage<>("foo"));
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel resultsChannel;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel delayedResults;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("flow3Input")
|
||||
private MessageChannel flow3Input;
|
||||
|
||||
@Autowired
|
||||
private AtomicReference<Object> eventHolder;
|
||||
|
||||
@Test
|
||||
public void testEventsFlow() {
|
||||
assertNull(this.eventHolder.get());
|
||||
this.flow3Input.send(new GenericMessage<>("2"));
|
||||
assertNotNull(this.eventHolder.get());
|
||||
assertEquals(4, this.eventHolder.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawApplicationEventListeningMessageProducer() {
|
||||
this.applicationContext.publishEvent(new TestApplicationEvent1());
|
||||
Message<?> receive = this.resultsChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive.getPayload(), instanceOf(TestApplicationEvent1.class));
|
||||
|
||||
this.applicationContext.publishEvent(new TestApplicationEvent2());
|
||||
receive = this.resultsChannel.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertThat(receive.getPayload(), instanceOf(TestApplicationEvent2.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelayRescheduling() {
|
||||
Message<?> receive = this.delayedResults.receive(10000);
|
||||
assertNotNull(receive);
|
||||
assertEquals("foo", receive.getPayload());
|
||||
assertEquals(1, messageGroupStore.getMessageGroupCount());
|
||||
assertEquals(0, messageGroupStore.getMessageCountForAllMessageGroups());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public AtomicReference<Object> eventHolder() {
|
||||
return new AtomicReference<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<MessagingEvent> eventListener() {
|
||||
return event -> eventHolder().set(event.getMessage().getPayload());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow flow3() {
|
||||
return IntegrationFlows.from("flow3Input")
|
||||
.handle(Integer.class, new GenericHandler<Integer>() {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFoo(String foo) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setFoo(Integer foo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Integer p, Map<String, Object> h) {
|
||||
return p * 2;
|
||||
}
|
||||
|
||||
})
|
||||
.handle(new ApplicationEventPublishingMessageHandler())
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<?> applicationListener() {
|
||||
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(TestApplicationEvent1.class);
|
||||
producer.setOutputChannel(resultsChannel());
|
||||
return producer;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public PollableChannel resultsChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow eventProducerFlow() {
|
||||
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
|
||||
producer.setEventTypes(TestApplicationEvent2.class);
|
||||
|
||||
return IntegrationFlows.from(producer)
|
||||
.channel(resultsChannel())
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow delayFlow() {
|
||||
return flow -> flow
|
||||
.delay(GROUP_ID, e -> e
|
||||
.messageStore(messageGroupStore)
|
||||
.id("delayer"))
|
||||
.channel(MessageChannels.queue("delayedResults"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final class TestApplicationEvent1 extends ApplicationEvent {
|
||||
|
||||
TestApplicationEvent1() {
|
||||
super("TestApplicationEvent1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final class TestApplicationEvent2 extends ApplicationEvent {
|
||||
|
||||
TestApplicationEvent2() {
|
||||
super("TestApplicationEvent2");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user