/*
* Copyright (c) 2011-2016 Pivotal Software Inc, All Rights Reserved.
*
* 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 reactor.core.publisher;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.atomic.AtomicReference;
import reactor.core.Exceptions;
import reactor.test.subscriber.AssertSubscriber;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;
import static reactor.core.scheduler.Schedulers.fromExecutor;
import static reactor.core.scheduler.Schedulers.fromExecutorService;
public class MonoPublishOnTest {
@Test
public void rejectedExecutionExceptionOnDataSignalExecutor()
throws InterruptedException {
int data = 1;
final AtomicReference<Throwable> throwableInOnOperatorError =
new AtomicReference<>();
final AtomicReference<Object> dataInOnOperatorError = new AtomicReference<>();
try {
CountDownLatch hookLatch = new CountDownLatch(1);
Hooks.onOperatorError((t, d) -> {
throwableInOnOperatorError.set(t);
dataInOnOperatorError.set(d);
hookLatch.countDown();
return t;
});
ExecutorService executor = newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AssertSubscriber<Integer> assertSubscriber = new AssertSubscriber<>();
Mono.just(data)
.publishOn(fromExecutorService(executor))
.doOnNext(s -> {
try {
latch.await();
}
catch (InterruptedException e) {
}
})
.publishOn(fromExecutor(executor))
.subscribe(assertSubscriber);
executor.shutdownNow();
assertSubscriber.assertNoValues()
.assertNoError()
.assertNotComplete();
hookLatch.await();
assertThat(throwableInOnOperatorError.get(),
instanceOf(RejectedExecutionException.class));
Assert.assertSame(dataInOnOperatorError.get(), data);
}
finally {
Hooks.resetOnOperatorError();
}
}
@Test
public void rejectedExecutionExceptionOnErrorSignalExecutor()
throws InterruptedException {
int data = 1;
Exception exception = new IllegalStateException();
final AtomicReference<Throwable> throwableInOnOperatorError =
new AtomicReference<>();
final AtomicReference<Object> dataInOnOperatorError = new AtomicReference<>();
try {
CountDownLatch hookLatch = new CountDownLatch(2);
Hooks.onOperatorError((t, d) -> {
throwableInOnOperatorError.set(t);
dataInOnOperatorError.set(d);
hookLatch.countDown();
return t;
});
ExecutorService executor = newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AssertSubscriber<Integer> assertSubscriber = new AssertSubscriber<>();
Mono.just(data)
.publishOn(fromExecutorService(executor))
.doOnNext(s -> {
try {
latch.await();
}
catch (InterruptedException e) {
throw Exceptions.propagate(exception);
}
})
.publishOn(fromExecutor(executor))
.subscribe(assertSubscriber);
executor.shutdownNow();
assertSubscriber.assertNoValues()
.assertNoError()
.assertNotComplete();
hookLatch.await();
assertThat(throwableInOnOperatorError.get(),
instanceOf(RejectedExecutionException.class));
Assert.assertSame(throwableInOnOperatorError.get()
.getSuppressed()[0], exception);
}
finally {
Hooks.resetOnOperatorError();
}
}
@Test
public void rejectedExecutionExceptionOnDataSignalExecutorService()
throws InterruptedException {
int data = 1;
final AtomicReference<Throwable> throwableInOnOperatorError =
new AtomicReference<>();
final AtomicReference<Object> dataInOnOperatorError = new AtomicReference<>();
try {
CountDownLatch hookLatch = new CountDownLatch(1);
Hooks.onOperatorError((t, d) -> {
throwableInOnOperatorError.set(t);
dataInOnOperatorError.set(d);
hookLatch.countDown();
return t;
});
ExecutorService executor = newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AssertSubscriber<Integer> assertSubscriber = new AssertSubscriber<>();
Mono.just(data)
.publishOn(fromExecutorService(executor))
.doOnNext(s -> {
try {
latch.await();
}
catch (InterruptedException e) {
}
})
.publishOn(fromExecutorService(executor))
.subscribe(assertSubscriber);
executor.shutdownNow();
assertSubscriber.assertNoValues()
.assertNoError()
.assertNotComplete();
hookLatch.await();
assertThat(throwableInOnOperatorError.get(),
instanceOf(RejectedExecutionException.class));
Assert.assertSame(dataInOnOperatorError.get(), data);
}
finally {
Hooks.resetOnOperatorError();
}
}
@Test
public void rejectedExecutionExceptionOnErrorSignalExecutorService()
throws InterruptedException {
int data = 1;
Exception exception = new IllegalStateException();
final AtomicReference<Throwable> throwableInOnOperatorError =
new AtomicReference<>();
final AtomicReference<Object> dataInOnOperatorError = new AtomicReference<>();
try {
CountDownLatch hookLatch = new CountDownLatch(2);
Hooks.onOperatorError((t, d) -> {
throwableInOnOperatorError.set(t);
dataInOnOperatorError.set(d);
hookLatch.countDown();
return t;
});
ExecutorService executor = newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(1);
AssertSubscriber<Integer> assertSubscriber = new AssertSubscriber<>();
Mono.just(data)
.publishOn(fromExecutorService(executor))
.doOnNext(s -> {
try {
latch.await();
}
catch (InterruptedException e) {
throw Exceptions.propagate(exception);
}
})
.publishOn(fromExecutorService(executor))
.subscribe(assertSubscriber);
executor.shutdownNow();
assertSubscriber.assertNoValues()
.assertNoError()
.assertNotComplete();
hookLatch.await();
assertThat(throwableInOnOperatorError.get(),
instanceOf(RejectedExecutionException.class));
Assert.assertSame(throwableInOnOperatorError.get()
.getSuppressed()[0], exception);
}
finally {
Hooks.resetOnOperatorError();
}
}
@Test
@Ignore
//FIXME the behavior is not failing fast anymore, find original issue and re-evaluate
public void rejectedExecutionSubscribeExecutorScheduler() {
CountDownLatch latch = new CountDownLatch(1);
ExecutorService executor = new ThreadPoolExecutor(1,
1,
0L,
MILLISECONDS,
new SynchronousQueue<>(),
new AbortPolicy());
try {
executor.submit(() -> {
try {
latch.await();
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
try {
Mono.just(1)
.publishOn(fromExecutor(executor))
.block();
Assert.fail("Bubbling RejectedExecutionException expected");
}
catch (Exception e) {
assertThat(Exceptions.unwrap(e), instanceOf(RejectedExecutionException.class));
}
}
finally {
latch.countDown();
executor.shutdownNow();
}
executor.shutdownNow();
}
@Test
@Ignore
//FIXME the behavior is not failing fast anymore, find original issue and re-evaluate
public void rejectedExecutionSubscribeExecutorServiceScheduler() {
CountDownLatch latch = new CountDownLatch(1);
ExecutorService executor = new ThreadPoolExecutor(1,
1,
0L,
MILLISECONDS,
new SynchronousQueue<>(),
new AbortPolicy());
try {
executor.submit(() -> {
try {
latch.await();
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
try {
Mono.just(1)
.publishOn(fromExecutor(executor))
.block();
Assert.fail("Bubbling RejectedExecutionException expected");
}
catch (Exception e) {
assertThat(Exceptions.unwrap(e), instanceOf(RejectedExecutionException.class));
}
}
finally {
latch.countDown();
executor.shutdownNow();
}
}
}