/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.camel.component.reactor.engine; import java.util.concurrent.atomic.AtomicLong; import org.apache.camel.Exchange; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.junit.Assert; import org.junit.Test; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import reactor.core.publisher.Flux; public class ReactorStreamsServiceSubscriberTest extends ReactorStreamsServiceTestSupport { @Test public void testSubscriber() throws Exception { context.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("reactive-streams:sub1") .to("mock:sub1"); from("reactive-streams:sub2") .to("mock:sub2"); from("timer:tick?period=50") .setBody() .simple("random(500)") .to("mock:sub3") .to("reactive-streams:pub"); } }); Subscriber<Integer> sub1 = crs.streamSubscriber("sub1", Integer.class); Subscriber<Integer> sub2 = crs.streamSubscriber("sub2", Integer.class); Publisher<Integer> pub = crs.fromStream("pub", Integer.class); pub.subscribe(sub1); pub.subscribe(sub2); context.start(); int count = 2; MockEndpoint e1 = getMockEndpoint("mock:sub1"); e1.expectedMinimumMessageCount(count); e1.assertIsSatisfied(); MockEndpoint e2 = getMockEndpoint("mock:sub2"); e2.expectedMinimumMessageCount(count); e2.assertIsSatisfied(); MockEndpoint e3 = getMockEndpoint("mock:sub3"); e3.expectedMinimumMessageCount(count); e3.assertIsSatisfied(); for (int i = 0; i < count; i++) { Exchange ex1 = e1.getExchanges().get(i); Exchange ex2 = e2.getExchanges().get(i); Exchange ex3 = e3.getExchanges().get(i); assertEquals(ex1.getIn().getBody(), ex2.getIn().getBody()); assertEquals(ex1.getIn().getBody(), ex3.getIn().getBody()); } } @Test public void testSingleConsumer() throws Exception { context.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("reactive-streams:singleConsumer") .process() .message(m -> m.setHeader("thread", Thread.currentThread().getId())) .to("mock:singleBucket"); } }); context.start(); Flux.range(0, 1000).subscribe( crs.streamSubscriber("singleConsumer", Number.class) ); MockEndpoint endpoint = getMockEndpoint("mock:singleBucket"); endpoint.expectedMessageCount(1000); endpoint.assertIsSatisfied(); Assert.assertEquals( 1, endpoint.getExchanges().stream() .map(x -> x.getIn().getHeader("thread", String.class)) .distinct() .count() ); // Ensure order is preserved when using a single consumer AtomicLong num = new AtomicLong(0); endpoint.getExchanges().stream() .map(x -> x.getIn().getBody(Long.class)) .forEach(n -> Assert.assertEquals(num.getAndIncrement(), n.longValue())); } @Test public void testMultipleConsumers() throws Exception { context.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { from("reactive-streams:multipleConsumers?concurrentConsumers=3") .process() .message(m -> m.setHeader("thread", Thread.currentThread().getId())) .to("mock:multipleBucket"); } }); context.start(); Flux.range(0, 1000).subscribe( crs.streamSubscriber("multipleConsumers", Number.class) ); MockEndpoint endpoint = getMockEndpoint("mock:multipleBucket"); endpoint.expectedMessageCount(1000); endpoint.assertIsSatisfied(); Assert.assertEquals( 3, endpoint.getExchanges().stream() .map(x -> x.getIn().getHeader("thread", String.class)) .distinct() .count() ); // Order cannot be preserved when using multiple consumers } }