/*
* Copyright (c) 2008-2017, Hazelcast, 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 com.hazelcast.executor;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.Member;
import com.hazelcast.core.MemberSelector;
import com.hazelcast.monitor.LocalExecutorStats;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class SingleNodeTest extends ExecutorServiceTestSupport {
private IExecutorService executor;
@Before
public void setUp() {
executor = createSingleNodeExecutorService("test", 1);
}
@Test
public void hazelcastInstanceAware_expectInjection() throws Throwable {
HazelcastInstanceAwareRunnable task = new HazelcastInstanceAwareRunnable();
try {
executor.submit(task).get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
@Test(expected = NullPointerException.class)
@SuppressWarnings("ConstantConditions")
public void submitNullTask_expectFailure() throws Exception {
executor.submit((Callable<?>) null);
}
@Test
public void submitBasicTask() throws Exception {
Callable<String> task = new BasicTestCallable();
Future future = executor.submit(task);
assertEquals(future.get(), BasicTestCallable.RESULT);
}
@Test(expected = RejectedExecutionException.class)
public void alwaysFalseMemberSelector_expectRejection() {
HazelcastInstanceAwareRunnable task = new HazelcastInstanceAwareRunnable();
executor.execute(task, new MemberSelector() {
@Override
public boolean select(Member member) {
return false;
}
});
}
@Test
public void executionCallback_notifiedOnSuccess() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Callable<String> task = new BasicTestCallable();
ExecutionCallback<String> executionCallback = new ExecutionCallback<String>() {
public void onResponse(String response) {
latch.countDown();
}
public void onFailure(Throwable t) {
}
};
executor.submit(task, executionCallback);
assertOpenEventually(latch);
}
@Test
public void executionCallback_notifiedOnFailure() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
FailingTestTask task = new FailingTestTask();
ExecutionCallback<String> executionCallback = new ExecutionCallback<String>() {
public void onResponse(String response) {
}
public void onFailure(Throwable t) {
latch.countDown();
}
};
executor.submit(task, executionCallback);
assertOpenEventually(latch);
}
@Test(expected = CancellationException.class)
public void timeOut_thenCancel() throws ExecutionException, InterruptedException {
SleepingTask task = new SleepingTask(1);
Future future = executor.submit(task);
try {
future.get(1, TimeUnit.MILLISECONDS);
fail("Should throw TimeoutException!");
} catch (TimeoutException expected) {
ignore(expected);
}
assertFalse(future.isDone());
assertTrue(future.cancel(true));
assertTrue(future.isCancelled());
assertTrue(future.isDone());
future.get();
}
@Test(expected = CancellationException.class)
public void cancelWhileQueued() throws ExecutionException, InterruptedException {
Callable task1 = new SleepingTask(100);
Future inProgressFuture = executor.submit(task1);
Callable task2 = new BasicTestCallable();
// this future should not be an instance of CompletedFuture,
// because even if we get an exception, isDone is returning true
Future queuedFuture = executor.submit(task2);
try {
assertFalse(queuedFuture.isDone());
assertTrue(queuedFuture.cancel(true));
assertTrue(queuedFuture.isCancelled());
assertTrue(queuedFuture.isDone());
} finally {
inProgressFuture.cancel(true);
}
queuedFuture.get();
}
@Test
public void isDoneAfterGet() throws Exception {
Callable<String> task = new BasicTestCallable();
Future future = executor.submit(task);
assertEquals(future.get(), BasicTestCallable.RESULT);
assertTrue(future.isDone());
}
@Test
public void issue129() throws Exception {
for (int i = 0; i < 1000; i++) {
Callable<String>
task1 = new BasicTestCallable(),
task2 = new BasicTestCallable();
Future<String>
future1 = executor.submit(task1),
future2 = executor.submit(task2);
assertEquals(future2.get(), BasicTestCallable.RESULT);
assertTrue(future2.isDone());
assertEquals(future1.get(), BasicTestCallable.RESULT);
assertTrue(future1.isDone());
}
}
@Test
public void issue292() throws Exception {
final BlockingQueue<Member> qResponse = new ArrayBlockingQueue<Member>(1);
executor.submit(new MemberCheck(), new ExecutionCallback<Member>() {
public void onResponse(Member response) {
qResponse.offer(response);
}
public void onFailure(Throwable t) {
}
});
assertNotNull(qResponse.poll(10, TimeUnit.SECONDS));
}
@Test(timeout = 10000)
public void taskSubmitsNestedTask() throws Exception {
Callable<String> task = new NestedExecutorTask();
executor.submit(task).get();
}
@Test
public void getManyTimesFromSameFuture() throws Exception {
Callable<String> task = new BasicTestCallable();
Future<String> future = executor.submit(task);
for (int i = 0; i < 4; i++) {
assertEquals(future.get(), BasicTestCallable.RESULT);
assertTrue(future.isDone());
}
}
@Test
public void invokeAll() throws Exception {
// only one task
ArrayList<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new BasicTestCallable());
List<Future<String>> futures = executor.invokeAll(tasks);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), BasicTestCallable.RESULT);
// more tasks
tasks.clear();
for (int i = 0; i < 1000; i++) {
tasks.add(new BasicTestCallable());
}
futures = executor.invokeAll(tasks);
assertEquals(futures.size(), 1000);
for (int i = 0; i < 1000; i++) {
assertEquals(futures.get(i).get(), BasicTestCallable.RESULT);
}
}
@Test
public void invokeAllTimeoutCancelled() throws Exception {
List<? extends Callable<Boolean>> singleTask = Collections.singletonList(new SleepingTask(0));
List<Future<Boolean>> futures = executor.invokeAll(singleTask, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), Boolean.TRUE);
List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
for (int i = 0; i < 1000; i++) {
tasks.add(new SleepingTask(i < 2 ? 0 : 20));
}
futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1000);
for (int i = 0; i < 1000; i++) {
if (i < 2) {
assertEquals(futures.get(i).get(), Boolean.TRUE);
} else {
try {
futures.get(i).get();
fail();
} catch (CancellationException expected) {
ignore(expected);
}
}
}
}
@Test
public void invokeAllTimeoutSuccess() throws Exception {
// only one task
ArrayList<Callable<String>> tasks = new ArrayList<Callable<String>>();
tasks.add(new BasicTestCallable());
List<Future<String>> futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1);
assertEquals(futures.get(0).get(), BasicTestCallable.RESULT);
// more tasks
tasks.clear();
for (int i = 0; i < 1000; i++) {
tasks.add(new BasicTestCallable());
}
futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
assertEquals(futures.size(), 1000);
for (int i = 0; i < 1000; i++) {
assertEquals(futures.get(i).get(), BasicTestCallable.RESULT);
}
}
/**
* Shutdown-related method behaviour when the cluster is running
*/
@Test
public void shutdownBehaviour() throws Exception {
// fresh instance, is not shutting down
assertFalse(executor.isShutdown());
assertFalse(executor.isTerminated());
executor.shutdown();
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// shutdownNow() should return an empty list and be ignored
List<Runnable> pending = executor.shutdownNow();
assertTrue(pending.isEmpty());
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// awaitTermination() should return immediately false
try {
boolean terminated = executor.awaitTermination(60L, TimeUnit.SECONDS);
assertFalse(terminated);
} catch (InterruptedException ie) {
fail("InterruptedException");
}
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
}
/**
* Shutting down the cluster should act as the ExecutorService shutdown
*/
@Test(expected = RejectedExecutionException.class)
public void clusterShutdown() {
shutdownNodeFactory();
sleepSeconds(2);
assertNotNull(executor);
assertTrue(executor.isShutdown());
assertTrue(executor.isTerminated());
// new tasks must be rejected
Callable<String> task = new BasicTestCallable();
executor.submit(task);
}
@Test
public void executorServiceStats() throws InterruptedException, ExecutionException {
final int iterations = 10;
LatchRunnable.latch = new CountDownLatch(iterations);
LatchRunnable runnable = new LatchRunnable();
for (int i = 0; i < iterations; i++) {
executor.execute(runnable);
}
assertOpenEventually(LatchRunnable.latch);
Future<Boolean> future = executor.submit(new SleepingTask(10));
future.cancel(true);
try {
future.get();
} catch (CancellationException ignored) {
}
assertTrueEventually(new AssertTask() {
@Override
public void run()
throws Exception {
LocalExecutorStats stats = executor.getLocalExecutorStats();
assertEquals(iterations + 1, stats.getStartedTaskCount());
assertEquals(iterations, stats.getCompletedTaskCount());
assertEquals(0, stats.getPendingTaskCount());
assertEquals(1, stats.getCancelledTaskCount());
}
});
}
static class LatchRunnable implements Runnable, Serializable {
static CountDownLatch latch;
@Override
public void run() {
latch.countDown();
}
}
}