diff --git a/pom.xml b/pom.xml index a2f5ce560..5b30b95af 100755 --- a/pom.xml +++ b/pom.xml @@ -187,6 +187,13 @@ commons-lang3 test + + + io.hawt + hawtio-embedded + 2.7.1 + test + diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHash.java index 17793112e..17faf9aa9 100644 --- a/src/main/java/net/openhft/chronicle/hash/ChronicleHash.java +++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHash.java @@ -25,6 +25,7 @@ import net.openhft.chronicle.set.ChronicleSet; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.lang.reflect.Type; import java.util.Map; import java.util.Set; import java.util.function.Consumer; @@ -96,6 +97,11 @@ public interface ChronicleHash, SC extends HashSegment */ Class keyClass(); + /** + * @return Type as a Class or UnresolvedType if unknown. + */ + Type keyType(); + /** * Returns a context to perform arbitrary operations with the given key in this store. * Conventionally, should be used in a try-with-resources block:
{@code
diff --git a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
index 1b35b95a2..1d613653b 100755
--- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
+++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java
@@ -38,6 +38,7 @@ import net.openhft.chronicle.map.ChronicleMapBuilder;
 import net.openhft.chronicle.values.Values;
 import net.openhft.chronicle.wire.Marshallable;
 import net.openhft.chronicle.wire.WireIn;
+import net.openhft.chronicle.wire.WireInternal;
 import net.openhft.chronicle.wire.WireOut;
 import org.jetbrains.annotations.NotNull;
 
@@ -46,6 +47,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.lang.ref.WeakReference;
+import java.lang.reflect.Type;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
@@ -93,7 +95,7 @@ public abstract class VanillaChronicleHash keyClass;
+    public Type keyClass;
     public SizeMarshaller keySizeMarshaller;
     public SizedReader keyReader;
     public DataAccess keyDataAccess;
@@ -247,7 +249,7 @@ public abstract class VanillaChronicleHash "keyClass").typeLiteral();
+        keyClass = wireIn.read(() -> "keyClass").lenientTypeLiteral();
         keySizeMarshaller = wireIn.read(() -> "keySizeMarshaller").object(SizeMarshaller.class);
         keyReader = wireIn.read(() -> "keyReader").object(SizedReader.class);
         keyDataAccess = wireIn.read(() -> "keyDataAccess").object(DataAccess.class);
@@ -682,8 +684,10 @@ public abstract class VanillaChronicleHash keyClass = keyClass();
         if (!keyClass.isInstance(key)) {
-            // key.getClass will cause NPE exactly as needed
+            if (key == null)
+                throw new NullPointerException("null key not supported");
             throw new ClassCastException(toIdentityString() + ": Key must be a " +
                     keyClass.getName() + " but was a " + key.getClass());
         }
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/ByteableDataAccess.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/ByteableDataAccess.java
index 4f98486ad..e417cbecd 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/ByteableDataAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/ByteableDataAccess.java
@@ -23,6 +23,8 @@ import net.openhft.chronicle.hash.serialization.DataAccess;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.lang.reflect.Type;
+
 public class ByteableDataAccess extends InstanceCreatingMarshaller
         implements DataAccess, Data {
 
@@ -31,7 +33,7 @@ public class ByteableDataAccess extends InstanceCreatingMars
      */
     private transient T instance;
 
-    public ByteableDataAccess(Class tClass) {
+    public ByteableDataAccess(Type tClass) {
         super(tClass);
     }
 
@@ -91,6 +93,6 @@ public class ByteableDataAccess extends InstanceCreatingMars
 
     @Override
     public DataAccess copy() {
-        return new ByteableDataAccess<>(tClass());
+        return new ByteableDataAccess<>(tType());
     }
 }
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/BytesMarshallableDataAccess.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/BytesMarshallableDataAccess.java
index ef7598fdd..ee2d02452 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/BytesMarshallableDataAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/BytesMarshallableDataAccess.java
@@ -23,6 +23,8 @@ import net.openhft.chronicle.wire.WireIn;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.lang.reflect.Type;
+
 import static net.openhft.chronicle.hash.serialization.impl.DefaultElasticBytes.DEFAULT_BYTES_CAPACITY;
 
 public class BytesMarshallableDataAccess
@@ -42,7 +44,7 @@ public class BytesMarshallableDataAccess
         this(tClass, DEFAULT_BYTES_CAPACITY);
     }
 
-    private BytesMarshallableDataAccess(Class tClass, long bytesCapacity) {
+    private BytesMarshallableDataAccess(Type tClass, long bytesCapacity) {
         super(tClass);
         initTransients(bytesCapacity);
     }
@@ -140,6 +142,6 @@ public class BytesMarshallableDataAccess
 
     @Override
     public DataAccess copy() {
-        return new BytesMarshallableDataAccess<>(tClass(), bytes.realCapacity());
+        return new BytesMarshallableDataAccess<>(tType(), bytes.realCapacity());
     }
 }
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/ExternalBytesMarshallableDataAccess.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/ExternalBytesMarshallableDataAccess.java
index 000183c7f..6f8f8f877 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/ExternalBytesMarshallableDataAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/ExternalBytesMarshallableDataAccess.java
@@ -27,6 +27,8 @@ import net.openhft.chronicle.wire.WireOut;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.lang.reflect.Type;
+
 import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
 import static net.openhft.chronicle.hash.serialization.impl.DefaultElasticBytes.DEFAULT_BYTES_CAPACITY;
 
@@ -53,7 +55,7 @@ public class ExternalBytesMarshallableDataAccess extends InstanceCreatingMars
     }
 
     private ExternalBytesMarshallableDataAccess(
-            Class tClass, SizedReader reader, BytesWriter writer,
+            Type tClass, SizedReader reader, BytesWriter writer,
             long bytesCapacity) {
         super(tClass);
         this.writer = writer;
@@ -125,7 +127,7 @@ public class ExternalBytesMarshallableDataAccess extends InstanceCreatingMars
     @Override
     public DataAccess copy() {
         return new ExternalBytesMarshallableDataAccess<>(
-                tClass(), copyIfNeeded(reader), copyIfNeeded(writer), bytes.realCapacity());
+                tType(), copyIfNeeded(reader), copyIfNeeded(writer), bytes.realCapacity());
     }
 
     @Override
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java
index 7dd822762..2d623495c 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java
@@ -26,6 +26,8 @@ import net.openhft.chronicle.wire.WireIn;
 import net.openhft.chronicle.wire.WireOut;
 import org.jetbrains.annotations.NotNull;
 
+import java.lang.reflect.Type;
+
 /**
  * Holds new instance creation logic, common for many {@link DataAccess} and {@link SizedReader}
  * implementations
@@ -34,7 +36,7 @@ import org.jetbrains.annotations.NotNull;
  */
 public abstract class InstanceCreatingMarshaller implements Marshallable {
 
-    private Class tClass;
+    private Type tClass;
 
     /**
      * Constructor for use in subclasses.
@@ -45,11 +47,19 @@ public abstract class InstanceCreatingMarshaller implements Marshallable {
         this.tClass = tClass;
     }
 
+    protected InstanceCreatingMarshaller(Type tClass) {
+        this.tClass = tClass;
+    }
+
     /**
      * Returns the class of objects deserialized.
      */
     protected Class tClass() {
-        return tClass;
+        return (Class) tClass;
+    }
+
+    protected Type tType() {
+        return  tClass;
     }
 
     /**
@@ -63,7 +73,7 @@ public abstract class InstanceCreatingMarshaller implements Marshallable {
      */
     protected T createInstance() {
         try {
-            return ObjectUtils.newInstance(tClass);
+            return ObjectUtils.newInstance(tClass());
         } catch (Exception e) {
             throw new IllegalStateException("Some of default marshallers, chosen for the type\n" +
                     tClass + " by default, delegate to \n" +
@@ -80,7 +90,7 @@ public abstract class InstanceCreatingMarshaller implements Marshallable {
     @Override
     public void readMarshallable(@NotNull WireIn wireIn) {
         //noinspection unchecked
-        tClass = wireIn.read(() -> "tClass").typeLiteral();
+        tClass = wireIn.read(() -> "tClass").lenientTypeLiteral();
     }
 
     @Override
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/NotReusingSizedMarshallableDataAccess.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/NotReusingSizedMarshallableDataAccess.java
index 82e655804..83f4bbd47 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/NotReusingSizedMarshallableDataAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/NotReusingSizedMarshallableDataAccess.java
@@ -20,13 +20,16 @@ import net.openhft.chronicle.hash.serialization.DataAccess;
 import net.openhft.chronicle.hash.serialization.SizedReader;
 import net.openhft.chronicle.hash.serialization.SizedWriter;
 
+import java.lang.reflect.Type;
+
 import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
+import static net.openhft.chronicle.hash.serialization.impl.DefaultElasticBytes.DEFAULT_BYTES_CAPACITY;
 
 public class NotReusingSizedMarshallableDataAccess extends SizedMarshallableDataAccess {
 
     public NotReusingSizedMarshallableDataAccess(
-            Class tClass, SizedReader sizedReader, SizedWriter sizedWriter) {
-        super(tClass, sizedReader, sizedWriter);
+            Type tClass, SizedReader sizedReader, SizedWriter sizedWriter) {
+        super(tClass, sizedReader, sizedWriter, DEFAULT_BYTES_CAPACITY);
     }
 
     @Override
@@ -37,6 +40,6 @@ public class NotReusingSizedMarshallableDataAccess extends SizedMarshallableD
     @Override
     public DataAccess copy() {
         return new NotReusingSizedMarshallableDataAccess<>(
-                tClass(), copyIfNeeded(sizedReader()), copyIfNeeded(sizedWriter()));
+                tType(), copyIfNeeded(sizedReader()), copyIfNeeded(sizedWriter()));
     }
 }
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/SizedMarshallableDataAccess.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/SizedMarshallableDataAccess.java
index 623d00c8c..57b790df3 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/SizedMarshallableDataAccess.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/SizedMarshallableDataAccess.java
@@ -26,6 +26,8 @@ import net.openhft.chronicle.wire.WireOut;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.lang.reflect.Type;
+
 import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
 import static net.openhft.chronicle.hash.serialization.impl.DefaultElasticBytes.DEFAULT_BYTES_CAPACITY;
 
@@ -52,8 +54,8 @@ public class SizedMarshallableDataAccess extends InstanceCreatingMarshaller tClass, SizedReader sizedReader, SizedWriter sizedWriter,
+    protected SizedMarshallableDataAccess(
+            Type tClass, SizedReader sizedReader, SizedWriter sizedWriter,
             long bytesCapacity) {
         super(tClass);
         this.sizedWriter = sizedWriter;
@@ -151,15 +153,15 @@ public class SizedMarshallableDataAccess extends InstanceCreatingMarshaller copy() {
         return new SizedMarshallableDataAccess<>(
-                tClass(), copyIfNeeded(sizedReader), copyIfNeeded(sizedWriter),
+                tType(), copyIfNeeded(sizedReader), copyIfNeeded(sizedWriter),
                 bytes.realCapacity());
     }
 
     @Override
     public void readMarshallable(@NotNull WireIn wireIn) {
         super.readMarshallable(wireIn);
-        sizedReader = wireIn.read(() -> "sizedReader").typedMarshallable();
-        sizedWriter = wireIn.read(() -> "sizedWriter").typedMarshallable();
+        sizedReader = wireIn.read(() -> "sizedReader").object(SizedReader.class);
+        sizedWriter = wireIn.read(() -> "sizedWriter").object(SizedWriter.class);
         initTransients(DEFAULT_BYTES_CAPACITY);
     }
 
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
index c54ef22dc..6618dc2ac 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
@@ -27,6 +27,7 @@ import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.io.Serializable;
+import java.lang.reflect.Type;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 
@@ -207,5 +208,10 @@ public interface ChronicleMap extends ConcurrentMap,
      * @return the class of {@code }
      */
     Class valueClass();
+
+    /**
+     * @return the value Class or UnresolvedType if unknown.
+     */
+    Type valueType();
 }
 
diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
index c4b034353..78d93b377 100755
--- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java
@@ -44,6 +44,7 @@ import org.jetbrains.annotations.NotNull;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -73,7 +74,7 @@ public class VanillaChronicleMap
     public transient DefaultValueProvider defaultValueProvider;
     /////////////////////////////////////////////////
     // Value Data model
-    Class valueClass;
+    Type valueClass;
     /////////////////////////////////////////////////
     // Behavior
     transient boolean putReturnsNull;
@@ -116,7 +117,7 @@ public class VanillaChronicleMap
     protected void readMarshallableFields(@NotNull WireIn wireIn) {
         super.readMarshallableFields(wireIn);
 
-        valueClass = wireIn.read(() -> "valueClass").typeLiteral();
+        valueClass = wireIn.read(() -> "valueClass").lenientTypeLiteral();
         valueSizeMarshaller = wireIn.read(() -> "valueSizeMarshaller").object(SizeMarshaller.class);
         valueReader = wireIn.read(() -> "valueReader").object(SizedReader.class);
         valueDataAccess = wireIn.read(() -> "valueDataAccess").object(DataAccess.class);
@@ -185,6 +186,7 @@ public class VanillaChronicleMap
     }
 
     public final V checkValue(Object value) {
+        Class valueClass = valueClass();
         if (!valueClass.isInstance(value)) {
             throw new ClassCastException(toIdentityString() + ": Value must be a " +
                     valueClass.getName() + " but was a " + value.getClass());
@@ -206,11 +208,21 @@ public class VanillaChronicleMap
 
     @Override
     public Class keyClass() {
+        return (Class) keyClass;
+    }
+
+    @Override
+    public Type keyType() {
         return keyClass;
     }
 
     @Override
     public Class valueClass() {
+        return (Class) valueClass;
+    }
+
+    @Override
+    public Type valueType() {
         return valueClass;
     }
 
diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/data/bytes/EntryValueBytesData.java b/src/main/java/net/openhft/chronicle/map/impl/stage/data/bytes/EntryValueBytesData.java
index 0e89384d7..4d5e0322f 100644
--- a/src/main/java/net/openhft/chronicle/map/impl/stage/data/bytes/EntryValueBytesData.java
+++ b/src/main/java/net/openhft/chronicle/map/impl/stage/data/bytes/EntryValueBytesData.java
@@ -44,7 +44,7 @@ public class EntryValueBytesData extends AbstractData {
 
     @Stage("CachedEntryValue")
     private V cachedEntryValue =
-            mh.m().valueClass() == CharSequence.class ? (V) new StringBuilder() : null;
+            mh.m().valueType() == CharSequence.class ? (V) new StringBuilder() : null;
     @Stage("CachedEntryValue")
     private boolean cachedEntryValueRead = false;
 
diff --git a/src/main/java/net/openhft/chronicle/set/SetFromMap.java b/src/main/java/net/openhft/chronicle/set/SetFromMap.java
index 360d3db8e..c31204622 100644
--- a/src/main/java/net/openhft/chronicle/set/SetFromMap.java
+++ b/src/main/java/net/openhft/chronicle/set/SetFromMap.java
@@ -24,6 +24,7 @@ import net.openhft.chronicle.map.VanillaChronicleMap;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
+import java.lang.reflect.Type;
 import java.util.AbstractSet;
 import java.util.Collection;
 import java.util.Iterator;
@@ -134,6 +135,11 @@ class SetFromMap extends AbstractSet implements ChronicleSet {
         return m.keyClass();
     }
 
+    @Override
+    public Type keyType() {
+        return m.keyType();
+    }
+
     // TODO test queryContext methods
 
     @NotNull
diff --git a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
index 046036461..910662f8d 100644
--- a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
+++ b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull;
 import org.junit.Assert;
 
 import java.io.File;
+import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
@@ -243,6 +244,11 @@ public class ReplicationCheckingMap implements ChronicleMap {
         return map1.keyClass();
     }
 
+    @Override
+    public Type keyType() {
+        return map1.keyType();
+    }
+
     @Override
     public boolean forEachEntryWhile(Predicate> predicate) {
         return map1.forEachEntryWhile(predicate);
@@ -258,6 +264,11 @@ public class ReplicationCheckingMap implements ChronicleMap {
         return map1.valueClass();
     }
 
+    @Override
+    public Type valueType() {
+        return map1.valueType();
+    }
+
     @Override
     public V acquireUsing(@NotNull K key, V usingValue) {
         return map1.acquireUsing(key, usingValue);
diff --git a/src/test/java/net/openhft/chronicle/map/watcher/MapFileClassifier.java b/src/test/java/net/openhft/chronicle/map/watcher/MapFileClassifier.java
new file mode 100644
index 000000000..47be9776c
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/watcher/MapFileClassifier.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016-2019 Chronicle Software Ltd
+ */
+
+package net.openhft.chronicle.map.watcher;
+
+import net.openhft.chronicle.core.watcher.FileClassifier;
+import net.openhft.chronicle.core.watcher.FileManager;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class MapFileClassifier implements FileClassifier {
+    @Override
+    public FileManager classify(String base, String relative) {
+        Path path = Paths.get(base, relative);
+        if (Files.isRegularFile(path) && relative.endsWith(".cmap")) {
+            return new MapFileManager(base, relative);
+        }
+        return null;
+    }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/watcher/MapFileManager.java b/src/test/java/net/openhft/chronicle/map/watcher/MapFileManager.java
new file mode 100755
index 000000000..63fc2c33c
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/watcher/MapFileManager.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016-2019 Chronicle Software Ltd
+ */
+
+package net.openhft.chronicle.map.watcher;
+
+import net.openhft.chronicle.bytes.MappedBytes;
+import net.openhft.chronicle.bytes.MappedFile;
+import net.openhft.chronicle.core.Jvm;
+import net.openhft.chronicle.core.watcher.JMXFileManager;
+import net.openhft.chronicle.hash.impl.PersistedChronicleHashResources;
+import net.openhft.chronicle.hash.impl.SizePrefixedBlob;
+import net.openhft.chronicle.map.VanillaChronicleMap;
+import net.openhft.chronicle.wire.ReadAnyWire;
+import net.openhft.chronicle.wire.Wires;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.lang.reflect.Type;
+import java.nio.file.Paths;
+
+public class MapFileManager extends JMXFileManager implements MapFileManagerMBean {
+    private static final long TIME_OUT = 5_000;
+    private String header;
+    private Type keyClass;
+    private Type valueClass;
+    private long size;
+    private String name;
+    private long dataStoreSize;
+    private int segments;
+
+    public MapFileManager(String basePath, String relativePath) {
+        super(basePath, relativePath);
+    }
+
+    @Override
+    protected String type() {
+        return "maps";
+    }
+
+    private long lastUpdate = 0;
+
+    public String getHeader() {
+        update();
+        return header;
+    }
+
+    public String getKeyClass() {
+        return ""+keyClass;
+    }
+
+    public String getValueClass() {
+        return ""+valueClass;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public long getDataStoreSize() {
+        return dataStoreSize;
+    }
+
+    public int getSegments() {
+        return segments;
+    }
+
+    public long getLastUpdate() {
+        return lastUpdate;
+    }
+
+    private void update() {
+        long now = System.currentTimeMillis();
+        if (lastUpdate + TIME_OUT > now)
+            return;
+
+        File file = Paths.get(basePath, relativePath).toFile();
+
+        MappedFile mf;
+        try {
+            mf = MappedFile.mappedFile(file, file.length());
+        } catch (FileNotFoundException e) {
+            Jvm.warn().on(getClass(), e);
+            return;
+        }
+        try (MappedBytes bytes = MappedBytes.mappedBytes(mf)) {
+            int length = bytes.readInt(SizePrefixedBlob.SIZE_WORD_OFFSET);
+            if (!Wires.isReady(length)) {
+                header = "not ready";
+            } else {
+                bytes.readPositionRemaining(SizePrefixedBlob.SIZE_WORD_OFFSET, Wires.lengthOf(length) + 4);
+                header = Wires.fromSizePrefixedBlobs(bytes);
+
+                bytes.readPositionRemaining(SizePrefixedBlob.SELF_BOOTSTRAPPING_HEADER_OFFSET, Wires.lengthOf(length));
+                ReadAnyWire wire = new ReadAnyWire(bytes);
+                VanillaChronicleMap map = wire.getValueIn()
+                        .object(VanillaChronicleMap.class);
+                int headerEnd = SizePrefixedBlob.SELF_BOOTSTRAPPING_HEADER_OFFSET + Wires.lengthOf(length);
+                map.initBeforeMapping(file, mf.raf(), headerEnd, false);
+                map.createMappedStoreAndSegments(
+                        new PersistedChronicleHashResources(file));
+
+
+                keyClass = map.keyType();
+                valueClass = map.valueType();
+                size = map.longSize();
+                name = map.name();
+                dataStoreSize = map.dataStoreSize();
+                segments = map.segments();
+            }
+        } catch (Exception e) {
+            Jvm.warn().on(getClass(), "Unable to update", e);
+            header = e.toString();
+        } finally {
+            mf.release();
+            assert mf.refCount() == 0;
+        }
+    }
+}
diff --git a/src/test/java/net/openhft/chronicle/map/watcher/MapFileManagerMBean.java b/src/test/java/net/openhft/chronicle/map/watcher/MapFileManagerMBean.java
new file mode 100755
index 000000000..806136ef0
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/watcher/MapFileManagerMBean.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016-2019 Chronicle Software Ltd
+ */
+
+package net.openhft.chronicle.map.watcher;
+
+import java.lang.reflect.Type;
+import java.util.Set;
+
+@SuppressWarnings("unused")
+public interface MapFileManagerMBean {
+
+    public String getHeader();
+
+    public String getKeyClass();
+
+    public String getValueClass();
+
+//    public long getSize();
+
+    public String getName();
+
+    public long getDataStoreSize();
+
+    public int getSegments();
+
+    public long getLastUpdate();
+}
diff --git a/src/test/java/net/openhft/chronicle/map/watcher/MapWatcherMain.java b/src/test/java/net/openhft/chronicle/map/watcher/MapWatcherMain.java
new file mode 100644
index 000000000..395e6ab1d
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/watcher/MapWatcherMain.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016-2019 Chronicle Software Ltd
+ */
+
+package net.openhft.chronicle.map.watcher;
+
+import io.hawt.embedded.Main;
+import net.openhft.chronicle.core.Jvm;
+import net.openhft.chronicle.core.watcher.ClassifyingWatcherListener;
+import net.openhft.chronicle.core.watcher.FileSystemWatcher;
+
+import java.io.File;
+
+public class MapWatcherMain {
+    public static void main(String[] args) throws Exception {
+        FileSystemWatcher fsw = new FileSystemWatcher();
+        String absolutePath = new File(".").getAbsolutePath();
+        System.out.println("Watching " + absolutePath);
+        fsw.addPath(absolutePath);
+        ClassifyingWatcherListener listener = new ClassifyingWatcherListener();
+        listener.addClassifier(new MapFileClassifier());
+        fsw.addListener(listener);
+        fsw.start();
+
+        System.setProperty("hawtio.authenticationEnabled", "false");
+        Main main = new Main();
+        main.setWar(Jvm.userHome() +"/OpenHFT/hawtio-default-2.7.1.war");
+        main.run();
+    }
+}