diff --git a/src/main/java/net/openhft/chronicle/map/BuildVersion.java b/src/main/java/net/openhft/chronicle/map/BuildVersion.java index 34292116e..f1539d072 100644 --- a/src/main/java/net/openhft/chronicle/map/BuildVersion.java +++ b/src/main/java/net/openhft/chronicle/map/BuildVersion.java @@ -30,12 +30,12 @@ import java.io.Reader; */ class BuildVersion { - static String version = null; + private static String version = null; /** * @return version of ChronicleMap being used, or NULL if its not known */ - public synchronized static String readVersion() { + public synchronized static String version() { if (version != null) { return version; diff --git a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java index 075005fea..85f33d069 100644 --- a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java @@ -171,19 +171,20 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon inBuffer = allocateDirect(128).order(ByteOrder.nativeOrder()); inBytes = new ByteBufferBytes(inBuffer.slice()); - versionCheck(); + checkVersion(); } - private void versionCheck() { + private void checkVersion() { String serverVersion = serverApplicationVersion(); String clientVersion = clientVersion(); if (serverVersion.equals(clientVersion)) { - LOG.warn("The Chronicle Map Server and StalessClient are on different versions, " + - "although these version are backwards compatible, we" + - " suggest that you use the same version, server=" + serverApplicationVersion() + ", " + + LOG.warn("DIFFERENT CHRONICLE-MAP VERSIONS: The Chronicle-Map-Server and " + + "Stateless-Client are on different " + + "versions, " + + " we suggest that you use the same version, server=" + serverApplicationVersion() + ", " + "client=" + clientVersion); } } @@ -462,7 +463,7 @@ class StatelessChronicleMap implements ChronicleMap, Closeable, Clon @SuppressWarnings("WeakerAccess") @NotNull public String clientVersion() { - return BuildVersion.readVersion(); + return BuildVersion.version(); } public boolean isEmpty() { diff --git a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java index c9c42e15f..c7c870589 100644 --- a/src/main/java/net/openhft/chronicle/map/TcpReplicator.java +++ b/src/main/java/net/openhft/chronicle/map/TcpReplicator.java @@ -45,6 +45,7 @@ import static java.nio.channels.SelectionKey.*; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static net.openhft.chronicle.map.AbstractChannelReplicator.SIZE_OF_SIZE; import static net.openhft.chronicle.map.AbstractChannelReplicator.SIZE_OF_TRANSACTION_ID; +import static net.openhft.chronicle.map.BuildVersion.version; import static net.openhft.chronicle.map.StatelessChronicleMap.EventId.HEARTBEAT; interface Work { @@ -498,6 +499,28 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo throttle(channel); } + + private void checkVersions(final Attached attached) { + + final String localVersion = BuildVersion.version(); + final String remoteVersion = attached.serverVersion; + + if (!remoteVersion.equals(localVersion)) { + byte remoteIdentifier = attached.remoteIdentifier; + LOG.warn("DIFFERENT CHRONICLE-MAP VERSIONS : " + + "local-map=" + localVersion + + ", remote-map-id-" + remoteIdentifier + "=" + + remoteVersion + + + ", The Remote Chronicle Map with " + + "identifier=" + remoteIdentifier + + " and this Chronicle Map are on different " + + "versions, we suggest that you use the same version." + + ); + } + } + /** * used to exchange identifiers and timestamps and heartbeat intervals between the server and * client @@ -565,6 +588,8 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo writer.writeRemoteBootstrapTimestamp(replica.lastModificationTime(remoteIdentifier)); + writer.writeServerVersion(); + // tell the remote node, what are heartbeat interval is writer.writeRemoteHeartbeatInterval(heartBeatIntervalMillis); } @@ -575,8 +600,16 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo return; } + if (attached.serverVersion == null) { + attached.serverVersion = reader.readRemoteServerVersion(); + if (attached.serverVersion == null) + return; + + checkVersions(attached); + } + if (!attached.hasRemoteHeartbeatInterval) { - final long value = reader.remoteHeartbeatIntervalFromBuffer(); + final long value = reader.readRemoteHeartbeatIntervalFromBuffer(); if (value == Long.MIN_VALUE) return; @@ -920,6 +953,7 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo // true if its socket is a ServerSocket public boolean isServer; // the frequency the remote node will send a heartbeat public boolean handShakingComplete; + public String serverVersion; public long remoteHeartbeatInterval = heartBeatIntervalMillis; boolean isHandShakingComplete() { @@ -1005,6 +1039,9 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo in().writeLong(timeStampOfLastMessage); } + void writeServerVersion() { + in().write(String.format("%1$" + 64 + "s", version()).toCharArray()); + } /** * writes all the entries that have changed, to the buffer which will later be written to @@ -1356,7 +1393,20 @@ final class TcpReplicator extends AbstractChannelReplicator implements Clo return Long.MIN_VALUE; } - public long remoteHeartbeatIntervalFromBuffer() { + /** + * @return the timestamp or -1 if unsuccessful + */ + String readRemoteServerVersion() { + if (out.remaining() >= 64) { + char[] chars = new char[64]; + out.readFully(chars, 0, chars.length); + return new String(chars).trim(); + } else + return null; + } + + + public long readRemoteHeartbeatIntervalFromBuffer() { return (out.remaining() >= 8) ? out.readLong() : Long.MIN_VALUE; } } diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 690d6a2ea..f20666cb5 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -122,7 +122,7 @@ class VanillaChronicleMap, kClass = keyBuilder.eClass; keySizeMarshaller = keyBuilder.sizeMarshaller(); originalKeyReader = keyBuilder.reader(); - dataFileVersion = BuildVersion.readVersion(); + dataFileVersion = BuildVersion.version(); originalKeyInterop = (KI) keyBuilder.interop(); @@ -188,7 +188,7 @@ class VanillaChronicleMap, * @return the version of chronicle map that is currently running */ public String applicationVersion() { - return BuildVersion.readVersion(); + return BuildVersion.version(); } diff --git a/src/test/java/net/openhft/chronicle/map/BuildVersionTest.java b/src/test/java/net/openhft/chronicle/map/BuildVersionTest.java index 6eb8ac964..cd9913103 100644 --- a/src/test/java/net/openhft/chronicle/map/BuildVersionTest.java +++ b/src/test/java/net/openhft/chronicle/map/BuildVersionTest.java @@ -32,7 +32,7 @@ public class BuildVersionTest { @Test public void test() throws IOException, InterruptedException { // checks that we always get a version - Assert.assertNotNull(BuildVersion.readVersion()); + Assert.assertNotNull(BuildVersion.version()); } @@ -51,7 +51,7 @@ public class BuildVersionTest { expected.put(1, 1.0); String version = ((VanillaChronicleMap) expected).persistedDataVersion(); - Assert.assertNotNull(BuildVersion.readVersion(), version); + Assert.assertNotNull(BuildVersion.version(), version); } }