/*
* 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.util;
import com.hazelcast.config.Config;
import com.hazelcast.core.Cluster;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.test.AssertTask;
import com.hazelcast.test.HazelcastTestSupport;
import java.lang.reflect.Method;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
/**
* Base class for tests which have to change the {@link Clock} implementation, which works properly only in an isolated node.
*
* Use {@link #startIsolatedNode()} to create an isolated node, which will pick up the actual {@link ClockProperties}.
* Use {@link #shutdownIsolatedNode()} and {@link #resetClock()} to stop the node and cleanup the properties.
*
* Implementations of this class have to run in full isolation, so {@link com.hazelcast.test.HazelcastSerialClassRunner} and
* no usage of {@link com.hazelcast.test.annotation.ParallelTest}.
*/
public abstract class AbstractClockTest extends HazelcastTestSupport {
private static final int JUMP_AFTER_SECONDS = 15;
protected Object isolatedNode;
protected HazelcastInstance startNode() {
Config config = getConfig();
return Hazelcast.newHazelcastInstance(config);
}
protected void startIsolatedNode() {
if (isolatedNode != null) {
throw new IllegalStateException("There is already an isolated node running!");
}
Thread thread = Thread.currentThread();
ClassLoader tccl = thread.getContextClassLoader();
try {
FilteringClassLoader cl = new FilteringClassLoader(Collections.<String>emptyList(), "com.hazelcast");
thread.setContextClassLoader(cl);
Class<?> configClazz = cl.loadClass("com.hazelcast.config.Config");
Object config = configClazz.newInstance();
Method setClassLoader = configClazz.getDeclaredMethod("setClassLoader", ClassLoader.class);
setClassLoader.invoke(config, cl);
Class<?> hazelcastClazz = cl.loadClass("com.hazelcast.core.Hazelcast");
Method newHazelcastInstance = hazelcastClazz.getDeclaredMethod("newHazelcastInstance", configClazz);
isolatedNode = newHazelcastInstance.invoke(hazelcastClazz, config);
} catch (Exception e) {
throw new RuntimeException("Could not start isolated Hazelcast instance", e);
} finally {
thread.setContextClassLoader(tccl);
}
}
protected void shutdownIsolatedNode() {
if (isolatedNode == null) {
return;
}
try {
Class<?> instanceClass = isolatedNode.getClass();
Method method = instanceClass.getMethod("shutdown");
method.invoke(isolatedNode);
isolatedNode = null;
} catch (Exception e) {
throw new RuntimeException("Could not start shutdown Hazelcast instance", e);
}
}
protected static void setClockOffset(long offset) {
System.setProperty(ClockProperties.HAZELCAST_CLOCK_OFFSET, String.valueOf(offset));
}
protected static void setJumpingClock(long offset) {
System.setProperty(ClockProperties.HAZELCAST_CLOCK_IMPL, JumpingSystemClock.class.getName());
System.setProperty(ClockProperties.HAZELCAST_CLOCK_OFFSET, String.valueOf(offset));
System.setProperty(JumpingSystemClock.JUMP_AFTER_SECONDS_PROPERTY, String.valueOf(JUMP_AFTER_SECONDS));
}
protected static void resetClock() {
System.clearProperty(ClockProperties.HAZELCAST_CLOCK_IMPL);
System.clearProperty(ClockProperties.HAZELCAST_CLOCK_OFFSET);
System.clearProperty(JumpingSystemClock.JUMP_AFTER_SECONDS_PROPERTY);
}
protected static long getClusterTime(Object isolatedNode) {
try {
Method getCluster = isolatedNode.getClass().getMethod("getCluster");
Object cluster = getCluster.invoke(isolatedNode);
Method getClusterTime = cluster.getClass().getMethod("getClusterTime");
return ((Number) getClusterTime.invoke(cluster)).longValue();
} catch (Exception e) {
throw new RuntimeException("Could not get cluster time from Hazelcast instance", e);
}
}
protected static void assertClusterTime(HazelcastInstance expectedHz, Object isolatedNode) {
assertClusterTime(expectedHz.getCluster().getClusterTime(), isolatedNode);
}
protected static void assertClusterTime(long expected, Object isolatedNode) {
assertClusterTime(expected, getClusterTime(isolatedNode));
}
protected static void assertClusterTime(Object expectedIsolatedNode, HazelcastInstance hz) {
assertClusterTime(getClusterTime(expectedIsolatedNode), hz);
}
protected static void assertClusterTime(HazelcastInstance expectedHz, HazelcastInstance hz) {
assertClusterTime(expectedHz.getCluster().getClusterTime(), hz);
}
protected static void assertClusterTime(long expected, HazelcastInstance hz) {
assertClusterTime(expected, hz.getCluster().getClusterTime());
}
protected static void assertClusterSizeAlways(final int expected, HazelcastInstance hz) {
final Cluster cluster = hz.getCluster();
assertTrueAllTheTime(new AssertTask() {
@Override
public void run() throws Exception {
assertEquals("Cluster should be stable when system clock changes!", expected, cluster.getMembers().size());
}
}, JUMP_AFTER_SECONDS * 2);
}
private static void assertClusterTime(long expected, long actual) {
assertEquals("Cluster time should be (approx.) equal to master time!", expected, actual, 1000d);
}
}