diff --git a/build.gradle b/build.gradle
index 40088343c9..02d7546f73 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,6 +34,7 @@ allprojects {
maven { url 'https://repo.spring.io/libs-snapshot' }
}
maven { url 'https://repo.spring.io/libs-milestone' }
+ maven { url 'https://repo.spring.io/libs-staging-local' }
mavenCentral()
}
@@ -114,7 +115,7 @@ subprojects { subproject ->
mockitoVersion = '1.10.19'
mysqlVersion = '5.1.34'
nettyVersion = '4.0.27.Final'
- openJpaVersion = '2.4.0'
+ openJpaVersion = '2.4.1'
pahoMqttClientVersion = '1.0.2'
postgresVersion = '9.1-901-1.jdbc4'
reactorVersion = '2.5.0.BUILD-SNAPSHOT'
diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParserTests-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParserTests-context.xml
index f8c7e18ee1..7a665877e3 100644
--- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParserTests-context.xml
+++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParserTests-context.xml
@@ -3,18 +3,14 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:si-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:int="http://www.springframework.org/schema/integration"
- xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/integration/amqp http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
- http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
+ connection-factory="rabbitConnectionFactory" message-converter="testConverter"/>
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/ReactiveChannel.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/ReactiveChannel.java
index d38a438f4b..67efd16887 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/channel/ReactiveChannel.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/ReactiveChannel.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2016 the original author or authors.
+ * Copyright 2002-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
+ * 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,
@@ -25,10 +25,9 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.Assert;
-import reactor.core.publisher.EmitterProcessor;
+import reactor.core.publisher.DirectProcessor;
import reactor.core.subscriber.BaseSubscriber;
-import reactor.core.subscriber.SignalEmitter;
-import reactor.core.util.PlatformDependent;
+import reactor.core.subscriber.SubmissionEmitter;
/**
* @author Artem Bilan
@@ -38,19 +37,19 @@ public class ReactiveChannel implements MessageChannel, Publisher> {
private final Processor, Message>> processor;
- private final SignalEmitter> emitter;
+ private final SubmissionEmitter> emitter;
public ReactiveChannel() {
- this(EmitterProcessor.create(PlatformDependent.SMALL_BUFFER_SIZE, Integer.MAX_VALUE, false));
+ this(DirectProcessor.create());
}
public ReactiveChannel(Processor, Message>> processor) {
Assert.notNull(processor, "'processor' must not be null");
this.processor = processor;
- this.emitter = SignalEmitter.create(processor);
+ this.emitter = SubmissionEmitter.create(processor);
}
- Subscriber> asSubscriber() {
+ public Subscriber> asSubscriber() {
return new BaseSubscriber>() {
@Override
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/ConsumerEndpointFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/ConsumerEndpointFactoryBean.java
index 06bbbb20b0..583f02f21c 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/ConsumerEndpointFactoryBean.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/ConsumerEndpointFactoryBean.java
@@ -295,7 +295,6 @@ public class ConsumerEndpointFactoryBean
subscriber = (Subscriber>) this.handler;
}
else {
- //TODO errorConsumer, completeConsumer
subscriber = Subscribers.consumer(this.handler::handleMessage);
}
this.endpoint = new ReactiveEndpoint(channel, subscriber);
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java
index 4e5c7d9900..423791eeef 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java
@@ -307,7 +307,8 @@ public abstract class AbstractMethodAnnotationPostProcessor annotations) {
+ protected AbstractEndpoint doCreateEndpoint(MessageHandler handler, MessageChannel inputChannel,
+ List annotations) {
AbstractEndpoint endpoint;
if (inputChannel instanceof PollableChannel) {
PollingConsumer pollingConsumer = new PollingConsumer((PollableChannel) inputChannel, handler);
@@ -422,7 +423,7 @@ public abstract class AbstractMethodAnnotationPostProcessor annotations, AbstractReplyProducingMessageHandler handler) {
+ protected void setOutputChannelIfPresent(List annotations, AbstractReplyProducingMessageHandler handler) {
String outputChannelName = MessagingAnnotationUtils.resolveAttribute(annotations, "outputChannel", String.class);
if (StringUtils.hasText(outputChannelName)) {
handler.setOutputChannelName(outputChannelName);
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ReactiveEndpoint.java b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ReactiveEndpoint.java
index ad2e863263..b57ba75620 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ReactiveEndpoint.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ReactiveEndpoint.java
@@ -18,12 +18,21 @@ package org.springframework.integration.endpoint;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.springframework.integration.channel.MessagePublishingErrorHandler;
+import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.messaging.PollableChannel;
+import org.springframework.messaging.SubscribableChannel;
import org.springframework.util.Assert;
+import org.springframework.util.ErrorHandler;
-import reactor.core.subscriber.SignalEmitter;
+import reactor.core.publisher.DirectProcessor;
+import reactor.core.subscriber.SubscriberBarrier;
+import reactor.core.util.Exceptions;
/**
@@ -32,35 +41,142 @@ import reactor.core.subscriber.SignalEmitter;
*/
public class ReactiveEndpoint extends AbstractEndpoint {
- private final Publisher> inputChannel;
+ private final Publisher> publisher;
- private final Subscriber> subscriber;
+ private final SubscriberBarrier, Message>> subscriber;
+
+ private ErrorHandler errorHandler;
- private SignalEmitter> emitter;
@SuppressWarnings("unchecked")
public ReactiveEndpoint(MessageChannel inputChannel, Subscriber> subscriber) {
Assert.notNull(inputChannel);
Assert.notNull(subscriber);
if (inputChannel instanceof Publisher) {
- this.inputChannel = (Publisher>) inputChannel;
+ this.publisher = (Publisher>) inputChannel;
}
else {
- //TODO: Wrap all other channels to the Publisher>
- this.inputChannel = null;
+ this.publisher = adaptToPublisher(inputChannel);
+ }
+ this.subscriber = new SubscriberBarrier, Message>>(subscriber) {
+
+ @Override
+ protected void doNext(Message> message) {
+ try {
+ super.doNext(message);
+ }
+ catch (Exception e) {
+ Exceptions.throwIfFatal(e);
+ ReactiveEndpoint.this.errorHandler.handleError(e);
+ }
+ }
+
+ };
+ }
+
+ public void setErrorHandler(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ @Override
+ protected void onInit() throws Exception {
+ super.onInit();
+ if (this.errorHandler == null) {
+ Assert.notNull(getBeanFactory(), "BeanFactory is required");
+ this.errorHandler = new MessagePublishingErrorHandler(
+ new BeanFactoryChannelResolver(getBeanFactory()));
}
- this.subscriber = subscriber;
}
@Override
protected void doStart() {
- this.emitter = SignalEmitter.create(this.subscriber);
- this.inputChannel.subscribe(this.emitter);
+ this.publisher.subscribe(this.subscriber);
}
@Override
protected void doStop() {
- this.emitter.finish();
+ this.subscriber.cancel();
+ }
+
+ private Publisher> adaptToPublisher(MessageChannel inputChannel) {
+ if (inputChannel instanceof SubscribableChannel) {
+ return adaptSubscribableChannelToPublisher((SubscribableChannel) inputChannel);
+ }
+ else if (inputChannel instanceof PollableChannel) {
+ return adaptPollableChannelToPublisher((PollableChannel) inputChannel);
+ }
+ else {
+ throw new IllegalArgumentException("The 'inputChannel' must be an instance of SubscribableChannel or " +
+ "PollableChannel, not: " + inputChannel);
+ }
+ }
+
+ private Publisher> adaptSubscribableChannelToPublisher(SubscribableChannel inputChannel) {
+ return new SubscribableChannelPublisherAdapter(inputChannel);
+ }
+
+ private Publisher> adaptPollableChannelToPublisher(PollableChannel inputChannel) {
+ return null;
+ }
+
+
+ private static class SubscribableChannelPublisherAdapter
+ implements Publisher>, Subscriber>, Subscription {
+
+ private final DirectProcessor> delegate = DirectProcessor.create();
+
+ private final MessageHandler subscriberAdapter = this.delegate.connectEmitter()::accept;
+
+ private final SubscribableChannel channel;
+
+ private Subscriber super Message>> actualSubscriber;
+
+ private Subscription actualSubscription;
+
+
+ private SubscribableChannelPublisherAdapter(SubscribableChannel channel) {
+ this.channel = channel;
+ }
+
+ @Override
+ public void subscribe(Subscriber super Message>> subscriber) {
+ this.actualSubscriber = subscriber;
+ this.delegate.subscribe(this);
+ this.channel.subscribe(this.subscriberAdapter);
+ }
+
+ @Override
+ public void onSubscribe(Subscription subscription) {
+ this.actualSubscription = subscription;
+ this.actualSubscriber.onSubscribe(this);
+ }
+
+ @Override
+ public void onNext(Message> message) {
+ this.actualSubscriber.onNext(message);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ this.actualSubscriber.onError(t);
+ }
+
+ @Override
+ public void onComplete() {
+ this.actualSubscriber.onComplete();
+ }
+
+ @Override
+ public void request(long n) {
+ this.actualSubscription.request(n);
+ }
+
+ @Override
+ public void cancel() {
+ this.channel.unsubscribe(this.subscriberAdapter);
+ this.actualSubscription.cancel();
+ }
+
}
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java
index 606b33940a..1de9b8e0e4 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java
@@ -29,7 +29,6 @@ import java.util.concurrent.Future;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
-import org.reactivestreams.Publisher;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
@@ -46,7 +45,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.GatewayHeader;
import org.springframework.integration.context.IntegrationProperties;
@@ -85,11 +83,6 @@ import reactor.core.publisher.Mono;
public class GatewayProxyFactoryBean extends AbstractEndpoint
implements TrackableComponent, FactoryBean