diff --git a/README.md b/README.md index eded1ec69..4205dd28c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ *We can help you get Chronicle up and running in your organisation, we suggest you invite us in for consultancy, charged on an ad-hoc basis, we can discuss the best options tailored to your individual -requirements. - [Contact Us](http://openhft.net/support/)* +requirements. - [Contact Us](http://chronicle.software/support/)* -*Or you may already be using Chronicle and just want some help - [find out more..](http://openhft.net/support/)* +*Or you may already be using Chronicle and just want some help - [find out more..](http://chronicle.software/support/)* # Chronicle Map A low latency replicated Key Value Store across your network, with eventual consistency, persistence and performance. -![Chronicle Map](http://openhft.net/wp-content/uploads/2014/07/ChronicleMap_200px.png) +![Chronicle Map](http://chronicle.software/wp-content/uploads/2014/07/ChronicleMap_200px.png) #### Maven Artifact Download ```xml @@ -60,7 +60,7 @@ Click here to get the [Latest Version Number](http://search.maven.org/#search%7C * [Known Issues](https://github.com/OpenHFT/Chronicle-Map#known-issues) * [Stackoverflow](http://stackoverflow.com/tags/chronicle/info) * [Development Tasks - JIRA] (https://higherfrequencytrading.atlassian.net/browse/HCOLL) - * [Use Case Which include Chronicle Map] (http://openhft.net/products/chronicle-engine/) + * [Use Case Which include Chronicle Map] (http://chronicle.software/products/chronicle-engine/) #### Examples @@ -81,7 +81,7 @@ Click here to get the [Latest Version Number](http://search.maven.org/#search%7C Chronicle Map implements the interface `java.util.concurrent.ConcurrentMap`, however unlike the standard jdk implementations, ChronicleMap is both persistent and able to share your entries accross processes: -![](http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-diagram_04.jpg) +![](http://chronicle.software/wp-content/uploads/2014/07/Chronicle-Map-diagram_04.jpg) ## When to use HashMap, ConcurrentHashMap and ChronicleMap #### When to use HashMap @@ -118,7 +118,7 @@ SharedHashMap was the old name of what is now ChronicleMap. Since the last relea ## Getting Started #### Tutorial 1 - Creating an instance of Chronicle Map -[![ScreenShot](http://openhft.net/wp-content/uploads/2014/09/Screen-Shot-2014-10-14-at-17.49.36.png)](http://openhft.net/chronicle-map-video-tutorial-1/) +[![ScreenShot](http://chronicle.software/wp-content/uploads/2014/09/Screen-Shot-2014-10-14-at-17.49.36.png)](http://chronicle.software/chronicle-map-video-tutorial-1/) ### Simple Construction @@ -126,7 +126,7 @@ To download the JAR which contains Chronicle Map, we recommend you use maven, wh from [Maven Central](http://search.maven.org), once you have installed maven, all you have to do is add the following to your projects `pom.xml`: -![Maven](http://openhft.net/wp-content/uploads/2014/09/maven.gif) +![Maven](http://chronicle.software/wp-content/uploads/2014/09/maven.gif) ```xml @@ -595,7 +595,7 @@ If after some business logic, in our example after reading the 'issueDate' and ## Serialization -![Serialization](http://openhft.net/wp-content/uploads/2014/09/Serialization_01.jpg) +![Serialization](http://chronicle.software/wp-content/uploads/2014/09/Serialization_01.jpg) ChronicleMap stores your data into off heap memory, so when you give it a Key or Value, it will @@ -615,7 +615,7 @@ an example of how to do this can be found at "IntValue$$Native" for a single type or a number of types. ### Import/Export entries as JSON -![Import/Export](http://openhft.net/wp-content/uploads/2014/09/Export-import_04.jpg) +![Import/Export](http://chronicle.software/wp-content/uploads/2014/09/Export-import_04.jpg) ChronicleMap supports importing and exporting all the entries into a JSON encoded file. @@ -665,7 +665,7 @@ your JVM to crash. Close MUST BE the last thing that you do with the map. ChronicleMap supports both TCP and UDP replication -![TCP/IP Replication](http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-TCP-Replication_simple_02.jpg) +![TCP/IP Replication](http://chronicle.software/wp-content/uploads/2014/07/Chronicle-Map-TCP-Replication_simple_02.jpg) ### TCP / UDP Background. TCP/IP is a reliable protocol, what this means is that unless you have a network failure or hardware @@ -824,7 +824,7 @@ this, in other words its a bidirectional connection. ### Configuring Three Way TCP/IP Replication -![TCP/IP Replication 3Way](http://openhft.net/wp-content/uploads/2014/09/Screen-Shot-2014-10-27-at-18.19.05.png) +![TCP/IP Replication 3Way](http://chronicle.software/wp-content/uploads/2014/09/Screen-Shot-2014-10-27-at-18.19.05.png) Below is example how to set up tcpConfig for 3 host @@ -865,7 +865,7 @@ is dropped and re-established. # Multiple Chronicle Maps - Network Distributed -![Chronicle Maps Network Distributed](http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map_channels_diagram_02.jpg) +![Chronicle Maps Network Distributed](http://chronicle.software/wp-content/uploads/2014/07/Chronicle-Map_channels_diagram_02.jpg) ChronicleMap TCP Replication lets you distribute a single ChronicleMap, to a number of servers across your network. Replication is point to point and the data transfer is bidirectional, so in the @@ -1034,7 +1034,7 @@ favoriteColourServer1.close(); # Stateless Client -![](http://openhft.net/wp-content/uploads/2014/07/Chronicle-Map-remote-stateless-map_04_vB.jpg) +![](http://chronicle.software/wp-content/uploads/2014/07/Chronicle-Map-remote-stateless-map_04_vB.jpg) A stateless client is an instance of a `ChronicleMap` or a `ChronicleSet` that does not hold any data @@ -1043,7 +1043,7 @@ data holds all your data, the server can not it’s self be a stateless client. Your stateless client must be connected to the server via TCP/IP. - ![ChronicleMap](http://openhft.net/wp-content/uploads/2014/09/State-Transition_1-thread_02.jpg) + ![ChronicleMap](http://chronicle.software/wp-content/uploads/2014/09/State-Transition_1-thread_02.jpg) The stateless client delegates all your method calls to the remote server. The stateless client operations will block, in other words the stateless @@ -1126,7 +1126,7 @@ When calling the stateless client, you will get better throughput if you invoke by the server. Your stateless client will block until an acknowledgement has been received from the server that the event was processed. -![Chronicle Map](http://openhft.net/wp-content/uploads/2014/09/State-Transition_2-thread_03.jpg) +![Chronicle Map](http://chronicle.software/wp-content/uploads/2014/09/State-Transition_2-thread_03.jpg) When you are calling methods that return a value like get() this blocking adds no additional overhead, because you have to wait for the return value anyway, diff --git a/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java b/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java new file mode 100644 index 000000000..7c22f0bc9 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java @@ -0,0 +1,257 @@ +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.function.SerializableFunction; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Created by daniel on 22/04/15. + */ +public class FilePerKeyMap implements ChronicleMap { + private String dir; + private Path dirPath; + + public FilePerKeyMap(String dir) { + try { + dirPath = Paths.get(dir); + Files.createDirectories(dirPath); + } catch (IOException e) { + throw new AssertionError(e); + } + this.dir = dir; + } + + @Override + public int size() { + return (int)getFiles(dirPath).count(); + } + + @Override + public boolean isEmpty() { + return size()==0 ? true: false; + } + + @Override + public boolean containsKey(Object key) { + return getFiles(dirPath).anyMatch(p->p.getFileName().toString().equals(key)); + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public V get(Object key) { + checkTypeIsString(key); + + Path path = Paths.get(dir,(String)key); + return (V)getFileContents(path); + } + + @Override + public V put(K key, V value) { + checkTypeIsString(key, value); + + Path path = Paths.get(dir,(String)key); + String existingValue = getFileContents(path); + writeToFile(path, (String) value); + + return (V) existingValue; + } + + private Stream getFiles(Path path){ + try { + return Files.walk(path).filter(p -> !Files.isDirectory(p)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private String getFileContents(Path path){ + String existingValue = null; + if(Files.exists(path)) { + try { + existingValue = Files.readAllLines(path).stream().collect(Collectors.joining()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + return existingValue; + } + + private void writeToFile(Path path, String value){ + try { + Files.write(path, value.getBytes(), StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private void deleteFile(Path path){ + try { + Files.delete(path); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public V remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @Override + public void clear() { + getFiles(dirPath).forEach(this::deleteFile); + } + + @NotNull + @Override + public Set keySet() { + return null; + } + + @NotNull + @Override + public Collection values() { + return null; + } + + @NotNull + @Override + public Set> entrySet() { + return null; + } + + @Override + public V getUsing(K key, V usingValue) { + return null; + } + + @Override + public V acquireUsing(@NotNull K key, V usingValue) { + return null; + } + + @NotNull + @Override + public MapKeyContext acquireContext(@NotNull K key, @NotNull V usingValue) { + return null; + } + + @Override + public R getMapped(K key, @NotNull SerializableFunction function) { + return null; + } + + @Override + public V putMapped(K key, @NotNull UnaryOperator unaryOperator) { + return null; + } + + @Override + public void getAll(File toFile) throws IOException { + + } + + @Override + public void putAll(File fromFile) throws IOException { + + } + + @Override + public V newValueInstance() { + return null; + } + + @Override + public K newKeyInstance() { + return null; + } + + @Override + public Class valueClass() { + return null; + } + + @Override + public File file() { + return null; + } + + @Override + public long longSize() { + return 0; + } + + @Override + public MapKeyContext context(K key) { + return null; + } + + @Override + public Class keyClass() { + return null; + } + + @Override + public boolean forEachEntryWhile(Predicate> predicate) { + return false; + } + + @Override + public void forEachEntry(Consumer> action) { + + } + + @Override + public void close() { + + } + + @Override + public V putIfAbsent(K key, V value) { + return null; + } + + @Override + public boolean remove(Object key, Object value) { + return false; + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return false; + } + + @Override + public V replace(K key, V value) { + return null; + } + + private void checkTypeIsString(Object... objs) { + for(Object o : objs){ + if(!(o instanceof String)) + throw new AssertionError("FilePerKeyMap only accepts Key and Values " + + "of type String. Unsupported type: '" + o + "'"); + } + } +} diff --git a/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java b/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java new file mode 100644 index 000000000..21e32d748 --- /dev/null +++ b/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java @@ -0,0 +1,57 @@ +package net.openhft.chronicle.map; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * JUnit test class to support {@link net.openhft.chronicle.map.FilePerMapKeyTest} + */ +public class FilePerMapKeyTest { + /** + * FilePerMapKey only works with Strings + */ + @Test(expected = AssertionError.class) + public void testWrongType(){ + FilePerKeyMap map = new FilePerKeyMap<>("/tmp/filepermaptests"); + map.put(1, "test"); + } + + /** + * Testing the put + */ + @Test + public void testPut(){ + //There are no entries in the map so null should be returned + FilePerKeyMap map = new FilePerKeyMap<>("/tmp/filepermaptests"); + //just in case it hasn't been cleared up last time + map.clear(); + + String val = map.put("one", "test1"); + assertEquals(val, null); + + //After the entry has been written the value returned should be + //the previous value + val = map.put("one", "test2"); + assertEquals(val, "test1"); + + //Check that get returns the latest value + val = map.get("one"); + assertEquals("test2", val); + + assertTrue(map.containsKey("one")); + assertFalse(map.containsKey("two")); + + //Check the size is one + int size = map.size(); + assertEquals(1,size); + assertFalse(map.isEmpty()); + + map.clear(); + //Check the size is now 0 + size = map.size(); + assertEquals(0, size); + + assertTrue(map.isEmpty()); + } +}