/*
* 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.ignite.internal.util.future;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.IgniteFutureCancelledCheckedException;
import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException;
import org.apache.ignite.internal.processors.closure.GridClosureProcessor;
import org.apache.ignite.internal.processors.pool.PoolProcessor;
import org.apache.ignite.internal.util.typedef.CI1;
import org.apache.ignite.internal.util.typedef.CX1;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.GridTestKernalContext;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
/**
* Tests grid future adapter use cases.
*/
public class GridFutureAdapterSelfTest extends GridCommonAbstractTest {
/**
* @throws Exception If failed.
*/
public void testOnDone() throws Exception {
GridFutureAdapter<String> fut = new GridFutureAdapter<>();
fut.onDone();
assertNull(fut.get());
fut = new GridFutureAdapter<>();
fut.onDone("test");
assertEquals("test", fut.get());
fut = new GridFutureAdapter<>();
fut.onDone(new IgniteCheckedException("TestMessage"));
final GridFutureAdapter<String> callFut1 = fut;
GridTestUtils.assertThrows(log, new Callable<Object>() {
@Override public Object call() throws Exception {
return callFut1.get();
}
}, IgniteCheckedException.class, "TestMessage");
fut = new GridFutureAdapter<>();
fut.onDone("test", new IgniteCheckedException("TestMessage"));
final GridFutureAdapter<String> callFut2 = fut;
GridTestUtils.assertThrows(log, new Callable<Object>() {
@Override public Object call() throws Exception {
return callFut2.get();
}
}, IgniteCheckedException.class, "TestMessage");
fut = new GridFutureAdapter<>();
fut.onDone("test");
fut.onCancelled();
assertEquals("test", fut.get());
}
/**
* @throws Exception If failed.
*/
public void testOnCancelled() throws Exception {
GridTestUtils.assertThrows(log, new Callable<Object>() {
@Override public Object call() throws Exception {
GridFutureAdapter<String> fut = new GridFutureAdapter<>();
fut.onCancelled();
return fut.get();
}
}, IgniteFutureCancelledCheckedException.class, null);
GridTestUtils.assertThrows(log, new Callable<Object>() {
@Override public Object call() throws Exception {
GridFutureAdapter<String> fut = new GridFutureAdapter<>();
fut.onCancelled();
fut.onDone();
return fut.get();
}
}, IgniteFutureCancelledCheckedException.class, null);
}
/**
* @throws Exception If failed.
*/
public void testListenSyncNotify() throws Exception {
GridFutureAdapter<String> fut = new GridFutureAdapter<>();
int lsnrCnt = 10;
final CountDownLatch latch = new CountDownLatch(lsnrCnt);
final Thread runThread = Thread.currentThread();
final AtomicReference<Exception> err = new AtomicReference<>();
for (int i = 0; i < lsnrCnt; i++) {
fut.listen(new CI1<IgniteInternalFuture<String>>() {
@Override public void apply(IgniteInternalFuture<String> t) {
if (Thread.currentThread() != runThread)
err.compareAndSet(null, new Exception("Wrong notification thread: " + Thread.currentThread()));
latch.countDown();
}
});
}
fut.onDone();
assertEquals(0, latch.getCount());
if (err.get() != null)
throw err.get();
final AtomicBoolean called = new AtomicBoolean();
err.set(null);
fut.listen(new CI1<IgniteInternalFuture<String>>() {
@Override public void apply(IgniteInternalFuture<String> t) {
if (Thread.currentThread() != runThread)
err.compareAndSet(null, new Exception("Wrong notification thread: " + Thread.currentThread()));
called.set(true);
}
});
assertTrue(called.get());
if (err.get() != null)
throw err.get();
}
/**
* @throws Exception If failed.
*/
public void testListenNotify() throws Exception {
GridTestKernalContext ctx = new GridTestKernalContext(log);
ctx.setExecutorService(Executors.newFixedThreadPool(1));
ctx.setSystemExecutorService(Executors.newFixedThreadPool(1));
ctx.add(new PoolProcessor(ctx));
ctx.add(new GridClosureProcessor(ctx));
ctx.start();
try {
GridFutureAdapter<String> fut = new GridFutureAdapter<>();
int lsnrCnt = 10;
final CountDownLatch latch = new CountDownLatch(lsnrCnt);
final Thread runThread = Thread.currentThread();
for (int i = 0; i < lsnrCnt; i++) {
fut.listen(new CI1<IgniteInternalFuture<String>>() {
@Override public void apply(IgniteInternalFuture<String> t) {
assert Thread.currentThread() == runThread;
latch.countDown();
}
});
}
fut.onDone();
latch.await();
final CountDownLatch doneLatch = new CountDownLatch(1);
fut.listen(new CI1<IgniteInternalFuture<String>>() {
@Override public void apply(IgniteInternalFuture<String> t) {
assert Thread.currentThread() == runThread;
doneLatch.countDown();
}
});
assert doneLatch.getCount() == 0;
doneLatch.await();
}
finally {
ctx.stop(false);
}
}
/**
* Test futures chaining.
*
* @throws Exception In case of any exception.
*/
public void testChaining() throws Exception {
checkChaining(null);
ExecutorService exec = Executors.newFixedThreadPool(1);
try {
checkChaining(exec);
GridFinishedFuture<Integer> fut = new GridFinishedFuture<>(1);
IgniteInternalFuture<Object> chain = fut.chain(new CX1<IgniteInternalFuture<Integer>, Object>() {
@Override public Object applyx(IgniteInternalFuture<Integer> fut) throws IgniteCheckedException {
return fut.get() + 1;
}
}, exec);
assertEquals(2, chain.get());
}
finally {
exec.shutdown();
}
}
/**
* @param exec Executor for chain callback.
* @throws Exception If failed.
*/
@SuppressWarnings("ErrorNotRethrown")
private void checkChaining(ExecutorService exec) throws Exception {
final CX1<IgniteInternalFuture<Object>, Object> passThrough = new CX1<IgniteInternalFuture<Object>, Object>() {
@Override public Object applyx(IgniteInternalFuture<Object> f) throws IgniteCheckedException {
return f.get();
}
};
GridFutureAdapter<Object> fut = new GridFutureAdapter<>();
IgniteInternalFuture<Object> chain = exec != null ? fut.chain(passThrough, exec) : fut.chain(passThrough);
assertFalse(fut.isDone());
assertFalse(chain.isDone());
try {
chain.get(20);
fail("Expects timeout exception.");
}
catch (IgniteFutureTimeoutCheckedException e) {
info("Expected timeout exception: " + e.getMessage());
}
fut.onDone("result");
assertEquals("result", chain.get(1));
// Test exception re-thrown.
fut = new GridFutureAdapter<>();
chain = exec != null ? fut.chain(passThrough, exec) : fut.chain(passThrough);
fut.onDone(new ClusterGroupEmptyCheckedException("test exception"));
try {
chain.get();
fail("Expects failed with exception.");
}
catch (ClusterGroupEmptyCheckedException e) {
info("Expected exception: " + e.getMessage());
}
// Test error re-thrown.
fut = new GridFutureAdapter<>();
chain = exec != null ? fut.chain(passThrough, exec) : fut.chain(passThrough);
try {
fut.onDone(new StackOverflowError("test error"));
if (exec == null)
fail("Expects failed with error.");
}
catch (StackOverflowError e) {
info("Expected error: " + e.getMessage());
}
try {
chain.get();
fail("Expects failed with error.");
}
catch (StackOverflowError e) {
info("Expected error: " + e.getMessage());
}
}
/**
* @throws Exception If failed.
*/
public void testGet() throws Exception {
GridFutureAdapter<Object> unfinished = new GridFutureAdapter<>();
GridFutureAdapter<Object> finished = new GridFutureAdapter<>();
GridFutureAdapter<Object> cancelled = new GridFutureAdapter<>();
finished.onDone("Finished");
cancelled.onCancelled();
try {
unfinished.get(50);
assert false;
}
catch (IgniteFutureTimeoutCheckedException e) {
info("Caught expected exception: " + e);
}
Object o = finished.get();
assertEquals("Finished", o);
o = finished.get(1000);
assertEquals("Finished", o);
try {
cancelled.get();
assert false;
}
catch (IgniteFutureCancelledCheckedException e) {
info("Caught expected exception: " + e);
}
try {
cancelled.get(1000);
assert false;
}
catch (IgniteFutureCancelledCheckedException e) {
info("Caught expected exception: " + e);
}
}
}