/*
* 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.instance;
import com.hazelcast.cluster.ClusterState;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.impl.AllowedDuringPassiveState;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.TestHazelcastInstanceFactory;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import com.hazelcast.util.ExceptionUtil;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.internal.cluster.impl.AdvancedClusterStateTest.changeClusterStateEventually;
import static com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl_asyncInvokeOnPartitionTest.InvocationEntryProcessor.latch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class NodeStateTest extends HazelcastTestSupport {
@Test
public void nodeState_isActive_whenInstanceStarted() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
HazelcastInstance hz = factory.newHazelcastInstance();
assertEquals(NodeState.ACTIVE, getNode(hz).getState());
}
@Test
public void nodeState_isShutdown_whenInstanceShutdown() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
HazelcastInstance hz = factory.newHazelcastInstance();
Node node = getNode(hz);
hz.shutdown();
assertEquals(NodeState.SHUT_DOWN, node.getState());
}
@Test
public void nodeState_isShutdown_whenInstanceTerminated() {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
HazelcastInstance hz = factory.newHazelcastInstance();
Node node = getNode(hz);
hz.shutdown();
assertEquals(NodeState.SHUT_DOWN, node.getState());
}
@Test
public void multipleShutdowns_Allowed() throws InterruptedException {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
HazelcastInstance hz = factory.newHazelcastInstance();
Node node = getNode(hz);
for (int i = 0; i < 3; i++) {
node.shutdown(false);
}
}
@Test
public void concurrentShutdowns_Allowed() throws InterruptedException {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
HazelcastInstance hz = factory.newHazelcastInstance();
final Node node = getNode(hz);
Thread[] shutdownThreads = new Thread[3];
for (int i = 0; i < shutdownThreads.length; i++) {
Thread thread = new Thread() {
public void run() {
node.shutdown(false);
}
};
thread.start();
shutdownThreads[i] = thread;
}
for (Thread thread : shutdownThreads) {
thread.join(TimeUnit.MINUTES.toMillis(1));
}
}
@Test
public void shouldReject_NormalOperationInvocation_whilePassive() throws Exception {
InvocationTask task = new InvocationTask() {
@Override
public void invoke(NodeEngine nodeEngine) throws Exception {
Future<Object> future = nodeEngine.getOperationService()
.invokeOnPartition(null, new DummyOperation(), 1);
try {
future.get();
fail("Invocation should fail while node is passive!");
} catch (ExecutionException e) {
Throwable cause = e.getCause();
assertTrue("Cause: " + cause, cause instanceof HazelcastInstanceNotActiveException);
}
}
};
testInvocation_whilePassive(task);
}
@Test
public void shouldReject_NormalOperationExecution_whilePassive() throws Exception {
InvocationTask task = new InvocationTask() {
@Override
public void invoke(NodeEngine nodeEngine) throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Operation op = new DummyOperation() {
@Override
public void onExecutionFailure(Throwable e) {
latch.countDown();
}
@Override
public boolean returnsResponse() {
return false;
}
};
nodeEngine.getOperationService().run(op);
assertOpenEventually(latch);
}
};
testInvocation_whilePassive(task);
}
@Test
public void shouldAllow_AllowedOperationInvocation_whilePassive() throws Exception {
InvocationTask task = new InvocationTask() {
@Override
public void invoke(NodeEngine nodeEngine) throws Exception {
Future<Object> future = nodeEngine.getOperationService()
.invokeOnTarget(null, new DummyAllowedDuringPassiveStateOperation(), nodeEngine.getThisAddress());
future.get(1, TimeUnit.MINUTES);
}
};
testInvocation_whilePassive(task);
}
@Test
public void shouldAllow_AllowedOperationExecution_whilePassive() throws Exception {
InvocationTask task = new InvocationTask() {
@Override
public void invoke(NodeEngine nodeEngine) throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Operation op = new DummyAllowedDuringPassiveStateOperation() {
@Override
public void afterRun() throws Exception {
latch.countDown();
}
@Override
public boolean returnsResponse() {
return false;
}
};
nodeEngine.getOperationService().run(op);
assertOpenEventually(latch);
}
};
testInvocation_whilePassive(task);
}
private void testInvocation_whilePassive(InvocationTask invocationTask) throws Exception {
TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory();
final HazelcastInstance hz = factory.newHazelcastInstance();
final Node node = getNode(hz);
changeClusterStateEventually(hz, ClusterState.PASSIVE);
assertEquals(NodeState.PASSIVE, node.getState());
try {
invocationTask.invoke(getNodeEngineImpl(hz));
} catch (Throwable e) {
// countdown-latch on failure
latch.countDown();
throw ExceptionUtil.rethrow(e);
}
}
private interface InvocationTask {
void invoke(NodeEngine nodeEngine) throws Exception;
}
private static class DummyOperation extends Operation {
@Override
public void run() throws Exception {
}
}
private static class DummyAllowedDuringPassiveStateOperation extends Operation implements AllowedDuringPassiveState {
@Override
public void run() throws Exception {
}
}
}