mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
HCOLL-280 Allow users to define their own xStream serialisers
This commit is contained in:
@@ -28,6 +28,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
@@ -39,8 +40,8 @@ class JsonSerializer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StatelessChronicleMap.class);
|
||||
|
||||
static synchronized <K, V> void getAll(File toFile, Map<K, V> map) throws IOException {
|
||||
final XStream xstream = xStream(map);
|
||||
static synchronized <K, V> void getAll(File toFile, Map<K, V> map, List jsonConverters) throws IOException {
|
||||
final XStream xstream = xStream(map, jsonConverters);
|
||||
OutputStream outputStream = new FileOutputStream(toFile);
|
||||
if (toFile.getName().toLowerCase().endsWith(".gz"))
|
||||
outputStream = new GZIPOutputStream(outputStream);
|
||||
@@ -50,8 +51,9 @@ class JsonSerializer {
|
||||
}
|
||||
|
||||
|
||||
static synchronized <K, V> void putAll(File fromFile, Map<K, V> map) throws IOException {
|
||||
final XStream xstream = xStream(map);
|
||||
static synchronized <K, V> void putAll(File fromFile, Map<K, V> map, List jsonConverters)
|
||||
throws IOException {
|
||||
final XStream xstream = xStream(map, jsonConverters);
|
||||
|
||||
InputStream inputStream = new FileInputStream(fromFile);
|
||||
if (fromFile.getName().toLowerCase().endsWith(".gz"))
|
||||
@@ -61,7 +63,7 @@ class JsonSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
private static <K, V> XStream xStream(Map<K, V> map) {
|
||||
private static <K, V> XStream xStream(Map<K, V> map, List jsonConverters) {
|
||||
try {
|
||||
final XStream xstream = new XStream(new JettisonMappedXmlDriver());
|
||||
xstream.setMode(XStream.NO_REFERENCES);
|
||||
@@ -70,6 +72,18 @@ class JsonSerializer {
|
||||
registerChronicleMapConverter(map, xstream);
|
||||
xstream.registerConverter(new ByteBufferConverter());
|
||||
xstream.registerConverter(new DataValueConverter());
|
||||
|
||||
for (Object c : jsonConverters) {
|
||||
if (c instanceof Converter) {
|
||||
xstream.registerConverter((Converter) c);
|
||||
} else {
|
||||
LOG.warn("Skipping Converter of type class=" + c.getClass().getName() + " as " +
|
||||
" expecting an object of type com.thoughtworks.xstream.converters" +
|
||||
".Converter");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return xstream;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
logErrorSuggestXStreem(e);
|
||||
|
||||
@@ -80,16 +80,16 @@ import static net.openhft.lang.model.DataValueGenerator.firstPrimitiveFieldType;
|
||||
* {@code ChronicleMap} implementations with completely different properties.
|
||||
*
|
||||
* <p>{@code ChronicleMap} ("ChronicleMaps, created by {@code ChronicleMapBuilder}") currently
|
||||
* doesn't support resizing. That is why you <i>must</i> configure {@linkplain
|
||||
* #entries(long) number of entries} you are going to insert into the created map <i>at most</i>.
|
||||
* See {@link #entries(long)} method documentation for more information on this.
|
||||
* doesn't support resizing. That is why you <i>must</i> configure {@linkplain #entries(long) number
|
||||
* of entries} you are going to insert into the created map <i>at most</i>. See {@link
|
||||
* #entries(long)} method documentation for more information on this.
|
||||
*
|
||||
* <p>If you key or value type is not constantly sized and known to {@code ChronicleHashBuilder},
|
||||
* i. e. it is not a boxed primitive, data value generated interface, {@link Byteable}, etc. (see
|
||||
* the complete list TODO insert the link to the complete list),
|
||||
* you <i>must</i> provide the {@code ChronicleHashBuilder} with some information about you keys
|
||||
* or values: if they are constantly-sized, call {@link #constantKeySizeBySample(Object)}, otherwise
|
||||
* {@link ChronicleHashBuilder#averageKeySize(double)} method, accordingly for values.
|
||||
* <p>If you key or value type is not constantly sized and known to {@code ChronicleHashBuilder}, i.
|
||||
* e. it is not a boxed primitive, data value generated interface, {@link Byteable}, etc. (see the
|
||||
* complete list TODO insert the link to the complete list), you <i>must</i> provide the {@code
|
||||
* ChronicleHashBuilder} with some information about you keys or values: if they are
|
||||
* constantly-sized, call {@link #constantKeySizeBySample(Object)}, otherwise {@link
|
||||
* ChronicleHashBuilder#averageKeySize(double)} method, accordingly for values.
|
||||
*
|
||||
* @param <K> key type of the maps, produced by this builder
|
||||
* @param <V> value type of the maps, produced by this builder
|
||||
@@ -112,6 +112,9 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
|
||||
private static final double UNDEFINED_DOUBLE_CONFIG = Double.NaN;
|
||||
|
||||
|
||||
private List jsonConverters = new ArrayList();
|
||||
|
||||
private static boolean isDefined(double config) {
|
||||
return !Double.isNaN(config);
|
||||
}
|
||||
@@ -246,12 +249,12 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
* encoded (and each character takes 2 bytes on-heap), because default off-heap {@link String}
|
||||
* encoding is UTF-8 in {@code ChronicleMap}.)
|
||||
*
|
||||
* @param averageKeySize
|
||||
* @throws IllegalStateException {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @see #constantKeySizeBySample(Object)
|
||||
* @see #averageValueSize(double)
|
||||
* @see #actualChunkSize(int)
|
||||
* @param averageKeySize
|
||||
*/
|
||||
@Override
|
||||
public ChronicleMapBuilder<K, V> averageKeySize(double averageKeySize) {
|
||||
@@ -280,6 +283,30 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to a add a custom xStream converter, this converter may be used while chronicle maps
|
||||
* serialises/deserialized and object(s) to and from JSON. You should add a custom converter if
|
||||
* you wish to customise the JSON format, or support object(s) that can not be serialised into
|
||||
* JSON.
|
||||
*
|
||||
* The method signature purposely has a loose coupling to xStream, it exposes {@code Object}
|
||||
* parameter, rather than the com.thoughtworks.xstream.jsonConverters.Converter this is so that
|
||||
* the the {@code ChronicleMapBuilder} class does not require xStream artifact/jars, however the
|
||||
* parameter pass must be of type com.thoughtworks.xstream.jsonConverters.Converter as no other
|
||||
* type is currently accepted.
|
||||
*
|
||||
* @param converter an xStream converter of type com.thoughtworks.xstream.jsonConverters.Converter
|
||||
* @return this builder back
|
||||
* @see ChronicleMap#putAll(java.io.File)
|
||||
* @see ChronicleMap#putAll(java.io.File)
|
||||
*/
|
||||
public ChronicleMapBuilder<K, V> addJsonConverter(Object converter) {
|
||||
this.jsonConverters.add(converter);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private double averageKeySize() {
|
||||
return averageKeyOrValueSize(averageKeySize, keyBuilder);
|
||||
}
|
||||
@@ -289,12 +316,12 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
* created by this builder. If value size is always the same, call {@link
|
||||
* #constantValueSizeBySample(Object)} method instead of this one.
|
||||
*
|
||||
* <p>{@code ChronicleHashBuilder} implementation heuristically chooses
|
||||
* {@linkplain #actualChunkSize(int) the actual chunk size} based on this configuration and
|
||||
* the key size, that, however, might result to quite high internal fragmentation,
|
||||
* i. e. losses because only integral number of chunks could be allocated for the entry. If you
|
||||
* want to avoid this, you should manually configure the actual chunk size in addition to this
|
||||
* average value size configuration, which is anyway needed.
|
||||
* <p>{@code ChronicleHashBuilder} implementation heuristically chooses {@linkplain
|
||||
* #actualChunkSize(int) the actual chunk size} based on this configuration and the key size,
|
||||
* that, however, might result to quite high internal fragmentation, i. e. losses because only
|
||||
* integral number of chunks could be allocated for the entry. If you want to avoid this, you
|
||||
* should manually configure the actual chunk size in addition to this average value size
|
||||
* configuration, which is anyway needed.
|
||||
*
|
||||
* <p>If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size is
|
||||
* known statically, it is automatically accounted and shouldn't be specified by user.
|
||||
@@ -366,8 +393,8 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws IllegalStateException is sizes of both keys and values of maps created by this
|
||||
* builder are constant, hence chunk size shouldn't be
|
||||
* configured by user
|
||||
* builder are constant, hence chunk size shouldn't be configured
|
||||
* by user
|
||||
* @see #entryAndValueAlignment(Alignment)
|
||||
* @see #entries(long)
|
||||
* @see #maxChunksPerEntry(int)
|
||||
@@ -385,6 +412,10 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
return this;
|
||||
}
|
||||
|
||||
public List jsonConverters() {
|
||||
return this.jsonConverters;
|
||||
}
|
||||
|
||||
static class EntrySizeInfo {
|
||||
final double averageEntrySize;
|
||||
final int worstAlignment;
|
||||
@@ -444,8 +475,8 @@ public final class ChronicleMapBuilder<K, V> implements Cloneable,
|
||||
}
|
||||
|
||||
/**
|
||||
* This is needed, if chunkSize = constant entry size is not aligned, for entry alignment to
|
||||
* be always the same, we should _misalign_ the first chunk.
|
||||
* This is needed, if chunkSize = constant entry size is not aligned, for entry alignment to be
|
||||
* always the same, we should _misalign_ the first chunk.
|
||||
*/
|
||||
int specialEntrySpaceOffset(boolean replicated) {
|
||||
if (!constantlySizedEntries() || valueAlignment() == Alignment.NO_ALIGNMENT)
|
||||
|
||||
@@ -59,7 +59,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable, Clon
|
||||
private final ByteBuffer connectionOutBuffer = ByteBuffer.wrap(connectionByte);
|
||||
private final String name;
|
||||
|
||||
|
||||
private final List jsonConverters;
|
||||
private ByteBuffer outBuffer;
|
||||
private ByteBufferBytes outBytes;
|
||||
|
||||
@@ -139,6 +139,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable, Clon
|
||||
@NotNull final ChronicleMapBuilder chronicleMapBuilder) {
|
||||
|
||||
this.config = config;
|
||||
this.jsonConverters = chronicleMapBuilder.jsonConverters();
|
||||
|
||||
final BufferResizer outBufferResizer = new BufferResizer() {
|
||||
@Override
|
||||
@@ -191,12 +192,12 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable, Clon
|
||||
|
||||
@Override
|
||||
public void getAll(File toFile) throws IOException {
|
||||
JsonSerializer.getAll(toFile, this);
|
||||
JsonSerializer.getAll(toFile, this, jsonConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(File fromFile) throws IOException {
|
||||
JsonSerializer.putAll(fromFile, this);
|
||||
JsonSerializer.putAll(fromFile, this, jsonConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -102,6 +102,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
final Class nativeValueClass;
|
||||
final MultiMapFactory multiMapFactory;
|
||||
final int maxChunksPerEntry;
|
||||
private final List jsonConverters;
|
||||
transient Provider<BytesReader<K>> keyReaderProvider;
|
||||
transient Provider<KI> keyInteropProvider;
|
||||
transient Provider<BytesReader<V>> valueReaderProvider;
|
||||
@@ -119,6 +120,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
keySizeMarshaller = keyBuilder.sizeMarshaller();
|
||||
originalKeyReader = keyBuilder.reader();
|
||||
dataFileVersion = BuildVersion.version();
|
||||
jsonConverters = builder.jsonConverters();
|
||||
|
||||
originalKeyInterop = (KI) keyBuilder.interop();
|
||||
|
||||
@@ -339,8 +341,8 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
private long segmentSize() {
|
||||
long ss = (CACHE_LINES.align(sizeOfMultiMap() + sizeOfMultiMapBitSet(), BYTES)
|
||||
* multiMapsPerSegment())
|
||||
// the free list and 0+ dirty lists.
|
||||
+ numberOfBitSets() * sizeOfSegmentFreeListBitSets()
|
||||
// the free list and 0+ dirty lists.
|
||||
+ numberOfBitSets() * sizeOfSegmentFreeListBitSets()
|
||||
+ sizeOfEntrySpaceInSegment();
|
||||
if ((ss & 63L) != 0)
|
||||
throw new AssertionError();
|
||||
@@ -671,12 +673,12 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
|
||||
@Override
|
||||
public synchronized void getAll(File toFile) throws IOException {
|
||||
JsonSerializer.getAll(toFile, this);
|
||||
JsonSerializer.getAll(toFile, this, jsonConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void putAll(File fromFile) throws IOException {
|
||||
JsonSerializer.putAll(fromFile, this);
|
||||
JsonSerializer.putAll(fromFile, this, jsonConverters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user